diff options
869 files changed, 22151 insertions, 7694 deletions
diff --git a/Android.bp b/Android.bp index b075f5c0a435..7bdedc79943d 100644 --- a/Android.bp +++ b/Android.bp @@ -77,7 +77,6 @@ java_defaults { "core/java/android/app/ISearchManager.aidl", "core/java/android/app/ISearchManagerCallback.aidl", "core/java/android/app/IServiceConnection.aidl", - "core/java/android/app/ISmsAppService.aidl", "core/java/android/app/IStopUserCallback.aidl", "core/java/android/app/job/IJobCallback.aidl", "core/java/android/app/job/IJobScheduler.aidl", @@ -288,6 +287,7 @@ java_defaults { "core/java/android/service/carrier/ICarrierService.aidl", "core/java/android/service/carrier/ICarrierMessagingCallback.aidl", "core/java/android/service/carrier/ICarrierMessagingService.aidl", + "core/java/android/service/carrier/ICarrierMessagingClientService.aidl", "core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl", "core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl", "core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl", diff --git a/Android.mk b/Android.mk index 65d4d240fdcd..9c65948f4838 100644 --- a/Android.mk +++ b/Android.mk @@ -79,34 +79,6 @@ update-api: doc-comment-check-docs # ==== hiddenapi lists ======================================= ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) -.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) -$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ - PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS) -$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ - frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ - frameworks/base/config/hiddenapi-greylist.txt \ - frameworks/base/config/hiddenapi-greylist-max-p.txt \ - frameworks/base/config/hiddenapi-greylist-max-o.txt \ - frameworks/base/config/hiddenapi-force-blacklist.txt \ - $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \ - $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ - $(SOONG_HIDDENAPI_FLAGS) - frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ - --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \ - --greylist frameworks/base/config/hiddenapi-greylist.txt \ - --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ - --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \ - --greylist-max-o-ignore-conflicts \ - frameworks/base/config/hiddenapi-greylist-max-o.txt \ - --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \ - --output $@.tmp - $(call commit-change-for-toc,$@) - -$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \ - frameworks/base/tools/hiddenapi/merge_csv.py \ - $(PRIVATE_METADATA_INPUTS) - frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@ - $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)) $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA)) endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING new file mode 100644 index 000000000000..8a676e994081 --- /dev/null +++ b/api/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsCurrentApiSignatureTestCases" + } + ] +} diff --git a/api/current.txt b/api/current.txt index e9a7a608d8d5..18274a2a4c62 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26,6 +26,7 @@ package android { field public static final String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE"; field public static final String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE"; + field public static final String BIND_CARRIER_MESSAGING_CLIENT_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"; field @Deprecated public static final String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; @@ -41,7 +42,6 @@ package android { field public static final String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; - field public static final String BIND_SMS_APP_SERVICE = "android.permission.BIND_SMS_APP_SERVICE"; field public static final String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; @@ -224,6 +224,11 @@ package android { public static final class R.attr { ctor public R.attr(); + field public static final int __removed1 = 16844185; // 0x1010599 + field public static final int __removed2 = 16844186; // 0x101059a + field public static final int __removed3 = 16844187; // 0x101059b + field public static final int __removed4 = 16844188; // 0x101059c + field public static final int __removed5 = 16844189; // 0x101059d field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -484,10 +489,6 @@ package android { field public static final int dashGap = 16843175; // 0x10101a7 field public static final int dashWidth = 16843174; // 0x10101a6 field public static final int data = 16842798; // 0x101002e - field public static final int dataRetentionTime = 16844189; // 0x101059d - field public static final int dataSentOffDevice = 16844186; // 0x101059a - field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b - field public static final int dataUsedForMonetization = 16844188; // 0x101059c field public static final int datePickerDialogTheme = 16843948; // 0x10104ac field public static final int datePickerMode = 16843955; // 0x10104b3 field public static final int datePickerStyle = 16843612; // 0x101035c @@ -645,6 +646,7 @@ package android { field public static final int footerDividersEnabled = 16843311; // 0x101022f field public static final int forceDarkAllowed = 16844172; // 0x101058c field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521 + field public static final int forceUriPermissions = 16844197; // 0x10105a5 field public static final int foreground = 16843017; // 0x1010109 field public static final int foregroundGravity = 16843264; // 0x1010200 field public static final int foregroundServiceType = 16844191; // 0x101059f @@ -1507,7 +1509,6 @@ package android { field @Deprecated public static final int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 - field public static final int usageInfoRequired = 16844185; // 0x1010599 field public static final int use32bitAbi = 16844053; // 0x1010515 field public static final int useAppZygote = 16844184; // 0x1010598 field public static final int useDefaultMargins = 16843641; // 0x1010379 @@ -6210,11 +6211,6 @@ package android.app { method public void onSharedElementsReady(); } - public class SmsAppService extends android.app.Service { - ctor public SmsAppService(); - method public final android.os.IBinder onBind(android.content.Intent); - } - public class StatusBarManager { } @@ -6742,6 +6738,7 @@ package android.app.admin { method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>); method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean); method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean); + method public void setDefaultSmsApplication(@NonNull android.content.ComponentName, @NonNull String); method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>); method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence); method public void setEndUserSessionMessage(@NonNull android.content.ComponentName, @Nullable CharSequence); @@ -7252,6 +7249,7 @@ package android.app.backup { ctor public BackupManager(android.content.Context); method public void dataChanged(); method public static void dataChanged(String); + method @Nullable public android.os.UserHandle getUserForAncestralSerialNumber(long); method @Deprecated public int requestRestore(android.app.backup.RestoreObserver); } @@ -10267,7 +10265,6 @@ package android.content { field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED"; field public static final String ACTION_PASTE = "android.intent.action.PASTE"; - field public static final String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS"; field public static final String ACTION_PICK = "android.intent.action.PICK"; field public static final String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY"; field public static final String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED"; @@ -10393,7 +10390,6 @@ package android.content { field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE"; field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI"; field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; - field public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS"; field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER"; field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT"; field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY"; @@ -11358,8 +11354,8 @@ package android.content.pm { field public android.content.pm.ProviderInfo[] providers; field public android.content.pm.ActivityInfo[] receivers; field public android.content.pm.FeatureInfo[] reqFeatures; - field @Deprecated public String[] requestedPermissions; - field @Deprecated public int[] requestedPermissionsFlags; + field public String[] requestedPermissions; + field public int[] requestedPermissionsFlags; field public android.content.pm.ServiceInfo[] services; field public String sharedUserId; field public int sharedUserLabel; @@ -11367,7 +11363,6 @@ package android.content.pm { field public android.content.pm.SigningInfo signingInfo; field public String[] splitNames; field public int[] splitRevisionCodes; - field public android.content.pm.UsesPermissionInfo[] usesPermissions; field @Deprecated public int versionCode; field public String versionName; } @@ -11867,7 +11862,6 @@ package android.content.pm { field public String group; field public CharSequence nonLocalizedDescription; field @Deprecated public int protectionLevel; - field public boolean usageInfoRequired; } public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable { @@ -11879,6 +11873,7 @@ package android.content.pm { field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000 field public String authority; field public int flags; + field public boolean forceUriPermissions; field public boolean grantUriPermissions; field public int initOrder; field @Deprecated public boolean isSyncable; @@ -12058,28 +12053,6 @@ package android.content.pm { field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR; } - public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { - method public int describeContents(); - method public int getDataRetention(); - method public int getDataRetentionWeeks(); - method public int getDataSentOffDevice(); - method public int getDataSharedWithThirdParty(); - method public int getDataUsedForMonetization(); - method public int getFlags(); - method public String getPermission(); - field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR; - field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2 - field public static final int RETENTION_NOT_RETAINED = 1; // 0x1 - field public static final int RETENTION_SPECIFIED = 4; // 0x4 - field public static final int RETENTION_UNDEFINED = 0; // 0x0 - field public static final int RETENTION_UNLIMITED = 3; // 0x3 - field public static final int RETENTION_USER_SELECTED = 2; // 0x2 - field public static final int USAGE_NO = 3; // 0x3 - field public static final int USAGE_UNDEFINED = 0; // 0x0 - field public static final int USAGE_USER_TRIGGERED = 2; // 0x2 - field public static final int USAGE_YES = 1; // 0x1 - } - public final class VersionedPackage implements android.os.Parcelable { ctor public VersionedPackage(@NonNull String, int); ctor public VersionedPackage(@NonNull String, long); @@ -12357,8 +12330,10 @@ package android.content.res { public final class Resources.Theme { method public void applyStyle(int, boolean); method public void dump(int, String, String); + method public int[] getAttributeResolutionStack(@AttrRes int, @StyleRes int, @StyleRes int); method public int getChangingConfigurations(); method public android.graphics.drawable.Drawable getDrawable(@DrawableRes int) throws android.content.res.Resources.NotFoundException; + method @StyleRes public int getExplicitStyle(@Nullable android.util.AttributeSet); method public android.content.res.Resources getResources(); method @NonNull public android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]); method @NonNull public android.content.res.TypedArray obtainStyledAttributes(@StyleRes int, @NonNull @StyleableRes int[]) throws android.content.res.Resources.NotFoundException; @@ -14197,6 +14172,7 @@ package android.graphics { field public static final int DEPTH_POINT_CLOUD = 257; // 0x101 field public static final int FLEX_RGBA_8888 = 42; // 0x2a field public static final int FLEX_RGB_888 = 41; // 0x29 + field public static final int HEIC = 1212500294; // 0x48454946 field public static final int JPEG = 256; // 0x100 field public static final int NV16 = 16; // 0x10 field public static final int NV21 = 17; // 0x11 @@ -14215,13 +14191,16 @@ package android.graphics { field public static final int YV12 = 842094169; // 0x32315659 } - public final class Insets { + public final class Insets implements android.os.Parcelable { method @NonNull public static android.graphics.Insets add(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); + method public int describeContents(); method @NonNull public static android.graphics.Insets max(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); method @NonNull public static android.graphics.Insets min(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); method @NonNull public static android.graphics.Insets of(int, int, int, int); method @NonNull public static android.graphics.Insets of(@Nullable android.graphics.Rect); method @NonNull public static android.graphics.Insets subtract(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.Insets> CREATOR; field public static final android.graphics.Insets NONE; field public final int bottom; field public final int left; @@ -16513,6 +16492,7 @@ package android.hardware.biometrics { field public static final int BIOMETRIC_ERROR_LOCKOUT = 7; // 0x7 field public static final int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9; // 0x9 field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb + field public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe field public static final int BIOMETRIC_ERROR_NO_SPACE = 4; // 0x4 field public static final int BIOMETRIC_ERROR_TIMEOUT = 3; // 0x3 field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2 @@ -16535,8 +16515,8 @@ package android.hardware.biometrics { public static class BiometricPrompt.Builder { ctor public BiometricPrompt.Builder(android.content.Context); method public android.hardware.biometrics.BiometricPrompt build(); + method public android.hardware.biometrics.BiometricPrompt.Builder setAllowDeviceCredential(boolean); method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence); - method public android.hardware.biometrics.BiometricPrompt.Builder setEnableFallback(boolean); method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener); method public android.hardware.biometrics.BiometricPrompt.Builder setRequireConfirmation(boolean); method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence); @@ -19447,9 +19427,9 @@ package android.icu.text { method public static android.icu.text.BreakIterator getSentenceInstance(java.util.Locale); method public static android.icu.text.BreakIterator getSentenceInstance(android.icu.util.ULocale); method public abstract java.text.CharacterIterator getText(); - method public static android.icu.text.BreakIterator getTitleInstance(); - method public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale); - method public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale); + method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(); + method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale); + method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale); method public static android.icu.text.BreakIterator getWordInstance(); method public static android.icu.text.BreakIterator getWordInstance(java.util.Locale); method public static android.icu.text.BreakIterator getWordInstance(android.icu.util.ULocale); @@ -19466,7 +19446,7 @@ package android.icu.text { field public static final int KIND_CHARACTER = 0; // 0x0 field public static final int KIND_LINE = 2; // 0x2 field public static final int KIND_SENTENCE = 3; // 0x3 - field public static final int KIND_TITLE = 4; // 0x4 + field @Deprecated public static final int KIND_TITLE = 4; // 0x4 field public static final int KIND_WORD = 1; // 0x1 field public static final int WORD_IDEO = 400; // 0x190 field public static final int WORD_IDEO_LIMIT = 500; // 0x1f4 @@ -22644,6 +22624,7 @@ package android.location { method public int getCodeType(); method public int getConstellationType(); method public int getMultipathIndicator(); + method @NonNull public String getOtherCodeTypeName(); method public double getPseudorangeRateMetersPerSecond(); method public double getPseudorangeRateUncertaintyMetersPerSecond(); method public long getReceivedSvTimeNanos(); @@ -22669,10 +22650,11 @@ package android.location { field public static final int CODE_TYPE_A = 0; // 0x0 field public static final int CODE_TYPE_B = 1; // 0x1 field public static final int CODE_TYPE_C = 2; // 0x2 - field public static final int CODE_TYPE_CODELESS = 13; // 0xd field public static final int CODE_TYPE_I = 3; // 0x3 field public static final int CODE_TYPE_L = 4; // 0x4 field public static final int CODE_TYPE_M = 5; // 0x5 + field public static final int CODE_TYPE_N = 13; // 0xd + field public static final int CODE_TYPE_OTHER = 255; // 0xff field public static final int CODE_TYPE_P = 6; // 0x6 field public static final int CODE_TYPE_Q = 7; // 0x7 field public static final int CODE_TYPE_S = 8; // 0x8 @@ -23747,6 +23729,7 @@ package android.media { ctor public ExifInterface(@NonNull java.io.InputStream) throws java.io.IOException; method public double getAltitude(double); method @Nullable public String getAttribute(@NonNull String); + method @Nullable public byte[] getAttributeBytes(@NonNull String); method public double getAttributeDouble(@NonNull String, double); method public int getAttributeInt(@NonNull String, int); method @Nullable public long[] getAttributeRange(@NonNull String); @@ -23902,6 +23885,7 @@ package android.media { field public static final String TAG_USER_COMMENT = "UserComment"; field public static final String TAG_WHITE_BALANCE = "WhiteBalance"; field public static final String TAG_WHITE_POINT = "WhitePoint"; + field public static final String TAG_XMP = "Xmp"; field public static final String TAG_X_RESOLUTION = "XResolution"; field public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients"; field public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning"; @@ -24555,7 +24539,9 @@ package android.media { } public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint { + ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int); method public boolean covers(@NonNull android.media.MediaFormat); + method public boolean covers(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint); field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_100; field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_120; field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_200; @@ -24590,8 +24576,8 @@ package android.media { field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_50; field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_60; field public final int frameRate; - field public final int height; - field public final int width; + field public final long macroBlockRate; + field public final int macroBlocks; } public final class MediaCodecList { @@ -34202,7 +34188,6 @@ package android.os { } public static class Build.Partition { - ctor public Build.Partition(); method public long getBuildTimeMillis(); method @NonNull public String getFingerprint(); method @NonNull public String getName(); @@ -38361,11 +38346,7 @@ package android.provider { method @NonNull public static String getVolumeName(@NonNull android.net.Uri); method @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri); - method @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri); - method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); - method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); - method public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri); field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; field public static final String ACTION_REVIEW = "android.provider.action.REVIEW"; @@ -38636,14 +38617,14 @@ package android.provider { public static interface MediaStore.MediaColumns extends android.provider.BaseColumns { field @Deprecated public static final String DATA = "_data"; field public static final String DATE_ADDED = "date_added"; - field public static final String DATE_EXPIRES = "date_expires"; field public static final String DATE_MODIFIED = "date_modified"; field public static final String DISPLAY_NAME = "_display_name"; - field public static final String HASH = "_hash"; + field public static final String DOCUMENT_ID = "document_id"; field public static final String HEIGHT = "height"; + field public static final String INSTANCE_ID = "instance_id"; field public static final String IS_PENDING = "is_pending"; - field public static final String IS_TRASHED = "is_trashed"; field public static final String MIME_TYPE = "mime_type"; + field public static final String ORIGINAL_DOCUMENT_ID = "original_document_id"; field public static final String OWNER_PACKAGE_NAME = "owner_package_name"; field public static final String PRIMARY_DIRECTORY = "primary_directory"; field public static final String SECONDARY_DIRECTORY = "secondary_directory"; @@ -41360,6 +41341,11 @@ package android.service.carrier { field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR; } + public class CarrierMessagingClientService extends android.app.Service { + ctor public CarrierMessagingClientService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public abstract class CarrierMessagingService extends android.app.Service { ctor public CarrierMessagingService(); method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); @@ -41701,6 +41687,7 @@ package android.service.notification { public static class NotificationListenerService.Ranking { ctor public NotificationListenerService.Ranking(); + method public boolean canBubble(); method public boolean canShowBadge(); method public android.app.NotificationChannel getChannel(); method public int getImportance(); @@ -42454,6 +42441,7 @@ package android.system { method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static boolean access(String, int) throws android.system.ErrnoException; method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static void bind(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void chmod(String, int) throws android.system.ErrnoException; method public static void chown(String, int, int) throws android.system.ErrnoException; method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException; @@ -42522,6 +42510,7 @@ package android.system { method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException; method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method @Deprecated public static void setegid(int) throws android.system.ErrnoException; method public static void setenv(String, String, boolean) throws android.system.ErrnoException; method @Deprecated public static void seteuid(int) throws android.system.ErrnoException; @@ -45163,13 +45152,13 @@ package android.telephony { method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int); method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>); + field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL"; field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED"; field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE"; field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION"; - field public static final String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE"; field public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED"; field public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED"; field public static final int APPTYPE_CSIM = 4; // 0x4 @@ -49564,6 +49553,7 @@ package android.view { ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context); method public abstract android.view.LayoutInflater cloneInContext(android.content.Context); method public final android.view.View createView(String, String, android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException; + method @Nullable public final android.view.View createView(@NonNull android.content.Context, @NonNull String, @Nullable String, @Nullable android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException; method public static android.view.LayoutInflater from(android.content.Context); method public android.content.Context getContext(); method public final android.view.LayoutInflater.Factory getFactory(); @@ -49575,6 +49565,7 @@ package android.view { method public android.view.View inflate(org.xmlpull.v1.XmlPullParser, @Nullable android.view.ViewGroup, boolean); method protected android.view.View onCreateView(String, android.util.AttributeSet) throws java.lang.ClassNotFoundException; method protected android.view.View onCreateView(android.view.View, String, android.util.AttributeSet) throws java.lang.ClassNotFoundException; + method @Nullable public android.view.View onCreateView(@NonNull android.content.Context, @Nullable android.view.View, @NonNull String, @Nullable android.util.AttributeSet) throws java.lang.ClassNotFoundException; method public void setFactory(android.view.LayoutInflater.Factory); method public void setFactory2(android.view.LayoutInflater.Factory2); method public void setFilter(android.view.LayoutInflater.Filter); @@ -50320,6 +50311,8 @@ package android.view { method @android.view.ViewDebug.ExportedProperty(category="drawing") public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); + method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(); + method @NonNull public java.util.Map<java.lang.Integer,java.lang.Integer> getAttributeSourceResourceMap(); method @android.view.ViewDebug.ExportedProperty @Nullable public String[] getAutofillHints(); method public final android.view.autofill.AutofillId getAutofillId(); method public int getAutofillType(); @@ -50350,6 +50343,7 @@ package android.view { method public void getDrawingRect(android.graphics.Rect); method public long getDrawingTime(); method @android.view.ViewDebug.ExportedProperty(category="drawing") public float getElevation(); + method @StyleRes public int getExplicitStyle(); method @android.view.ViewDebug.ExportedProperty public boolean getFilterTouchesWhenObscured(); method @android.view.ViewDebug.ExportedProperty public boolean getFitsSystemWindows(); method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.NOT_FOCUSABLE, to="NOT_FOCUSABLE"), @android.view.ViewDebug.IntToString(from=android.view.View.FOCUSABLE, to="FOCUSABLE"), @android.view.ViewDebug.IntToString(from=android.view.View.FOCUSABLE_AUTO, to="FOCUSABLE_AUTO")}, category="focus") public int getFocusable(); @@ -50644,6 +50638,7 @@ package android.view { method public static int resolveSizeAndState(int, int, int); method public boolean restoreDefaultFocus(); method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>); + method public final void saveAttributeDataForStyleable(@NonNull android.content.Context, @NonNull int[], @Nullable android.util.AttributeSet, @NonNull android.content.res.TypedArray, int, int); method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>); method public void scheduleDrawable(@NonNull android.graphics.drawable.Drawable, @NonNull Runnable, long); method public void scrollBy(int, int); @@ -51121,6 +51116,7 @@ package android.view { method public int getScaledHoverSlop(); method public int getScaledMaximumDrawingCacheSize(); method public int getScaledMaximumFlingVelocity(); + method public int getScaledMinScalingSpan(); method public int getScaledMinimumFlingVelocity(); method public int getScaledOverflingDistance(); method public int getScaledOverscrollDistance(); @@ -52617,7 +52613,6 @@ package android.view.animation { public abstract class Animation implements java.lang.Cloneable { ctor public Animation(); ctor public Animation(android.content.Context, android.util.AttributeSet); - method public void addAnimationListener(android.view.animation.Animation.AnimationListener); method protected void applyTransformation(float, android.view.animation.Transformation); method public void cancel(); method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException; @@ -52642,7 +52637,6 @@ package android.view.animation { method public void initialize(int, int, int, int); method public boolean isFillEnabled(); method public boolean isInitialized(); - method public void removeAnimationListener(android.view.animation.Animation.AnimationListener); method public void reset(); method protected float resolveSize(int, float, int, int); method public void restrictDuration(long); @@ -54859,6 +54853,7 @@ package android.widget { method public void deferNotifyDataSetChanged(); method public void fling(int); method public android.widget.AbsListView.LayoutParams generateLayoutParams(android.util.AttributeSet); + method @ColorInt public int getBottomEdgeEffectColor(); method @android.view.ViewDebug.ExportedProperty(category="drawing") @ColorInt public int getCacheColorHint(); method public int getCheckedItemCount(); method public long[] getCheckedItemIds(); @@ -54873,6 +54868,7 @@ package android.widget { method @android.view.ViewDebug.ExportedProperty public android.view.View getSelectedView(); method public android.graphics.drawable.Drawable getSelector(); method public CharSequence getTextFilter(); + method @ColorInt public int getTopEdgeEffectColor(); method public int getTranscriptMode(); method protected void handleDataChanged(); method public boolean hasTextFilter(); @@ -54900,9 +54896,11 @@ package android.widget { method public void reclaimViews(java.util.List<android.view.View>); method public void scrollListBy(int); method public void setAdapter(android.widget.ListAdapter); + method public void setBottomEdgeEffectColor(@ColorInt int); method public void setCacheColorHint(@ColorInt int); method public void setChoiceMode(int); method public void setDrawSelectorOnTop(boolean); + method public void setEdgeEffectColor(@ColorInt int); method public void setFastScrollAlwaysVisible(boolean); method public void setFastScrollEnabled(boolean); method public void setFastScrollStyle(int); @@ -54921,6 +54919,7 @@ package android.widget { method public void setSmoothScrollbarEnabled(boolean); method public void setStackFromBottom(boolean); method public void setTextFilterEnabled(boolean); + method public void setTopEdgeEffectColor(@ColorInt int); method public void setTranscriptMode(int); method public void setVelocityScale(float); method public void smoothScrollBy(int, int); @@ -55250,6 +55249,7 @@ package android.widget { method public void performCompletion(); method protected void performFiltering(CharSequence, int); method public void performValidation(); + method public final void refreshAutoCompleteResults(); method protected void replaceText(CharSequence); method public <T extends android.widget.ListAdapter & android.widget.Filterable> void setAdapter(T); method public void setCompletionHint(CharSequence); @@ -55553,6 +55553,7 @@ package android.widget { ctor public EdgeEffect(android.content.Context); method public boolean draw(android.graphics.Canvas); method public void finish(); + method @Nullable public android.graphics.BlendMode getBlendMode(); method @ColorInt public int getColor(); method public int getMaxHeight(); method public boolean isFinished(); @@ -55560,8 +55561,10 @@ package android.widget { method public void onPull(float); method public void onPull(float, float); method public void onRelease(); + method public void setBlendMode(@Nullable android.graphics.BlendMode); method public void setColor(@ColorInt int); method public void setSize(int, int); + field public static final android.graphics.BlendMode DEFAULT_BLEND_MODE; } public class EditText extends android.widget.TextView { @@ -56011,6 +56014,7 @@ package android.widget { method @Nullable public android.view.View getAnchorView(); method @StyleRes public int getAnimationStyle(); method @Nullable public android.graphics.drawable.Drawable getBackground(); + method @Nullable public android.graphics.Rect getEpicenterBounds(); method public int getHeight(); method public int getHorizontalOffset(); method public int getInputMethodMode(); @@ -56037,6 +56041,7 @@ package android.widget { method public void setBackgroundDrawable(@Nullable android.graphics.drawable.Drawable); method public void setContentWidth(int); method public void setDropDownGravity(int); + method public void setEpicenterBounds(@Nullable android.graphics.Rect); method public void setHeight(int); method public void setHorizontalOffset(int); method public void setInputMethodMode(int); @@ -56297,6 +56302,7 @@ package android.widget { method public android.view.View getContentView(); method public float getElevation(); method @Nullable public android.transition.Transition getEnterTransition(); + method @Nullable public android.graphics.Rect getEpicenterBounds(); method @Nullable public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); @@ -56309,30 +56315,37 @@ package android.widget { method public int getWindowLayoutType(); method public boolean isAboveAnchor(); method public boolean isAttachedInDecor(); + method public boolean isClipToScreenEnabled(); method public boolean isClippingEnabled(); method public boolean isFocusable(); + method public boolean isLayoutInScreenEnabled(); method public boolean isOutsideTouchable(); method public boolean isShowing(); method public boolean isSplitTouchEnabled(); + method public boolean isTouchModal(); method public boolean isTouchable(); method public void setAnimationStyle(int); method public void setAttachedInDecor(boolean); method public void setBackgroundDrawable(android.graphics.drawable.Drawable); + method public void setClipToScreenEnabled(boolean); method public void setClippingEnabled(boolean); method public void setContentView(android.view.View); method public void setElevation(float); method public void setEnterTransition(@Nullable android.transition.Transition); + method public void setEpicenterBounds(@Nullable android.graphics.Rect); method public void setExitTransition(@Nullable android.transition.Transition); method public void setFocusable(boolean); method public void setHeight(int); method public void setIgnoreCheekPress(); method public void setInputMethodMode(int); + method public void setLayoutInScreenEnabled(boolean); method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener); method public void setOutsideTouchable(boolean); method public void setOverlapAnchor(boolean); method public void setSoftInputMode(int); method public void setSplitTouchEnabled(boolean); method public void setTouchInterceptor(android.view.View.OnTouchListener); + method public void setTouchModal(boolean); method public void setTouchable(boolean); method public void setWidth(int); method @Deprecated public void setWindowLayoutMode(int, int); @@ -56361,12 +56374,17 @@ package android.widget { ctor public ProgressBar(android.content.Context, android.util.AttributeSet); ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int); ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int, int); + method @Nullable public android.graphics.drawable.Drawable getCurrentDrawable(); method public android.graphics.drawable.Drawable getIndeterminateDrawable(); method @Nullable public android.content.res.ColorStateList getIndeterminateTintList(); method @Nullable public android.graphics.PorterDuff.Mode getIndeterminateTintMode(); method public android.view.animation.Interpolator getInterpolator(); method @android.view.ViewDebug.ExportedProperty(category="progress") public int getMax(); + method @Px public int getMaxHeight(); + method @Px public int getMaxWidth(); method @android.view.ViewDebug.ExportedProperty(category="progress") public int getMin(); + method @Px public int getMinHeight(); + method @Px public int getMinWidth(); method @android.view.ViewDebug.ExportedProperty(category="progress") public int getProgress(); method @Nullable public android.content.res.ColorStateList getProgressBackgroundTintList(); method @Nullable public android.graphics.PorterDuff.Mode getProgressBackgroundTintMode(); @@ -56390,7 +56408,11 @@ package android.widget { method public void setInterpolator(android.content.Context, @InterpolatorRes int); method public void setInterpolator(android.view.animation.Interpolator); method public void setMax(int); + method public void setMaxHeight(@Px int); + method public void setMaxWidth(@Px int); method public void setMin(int); + method public void setMinHeight(@Px int); + method public void setMinWidth(@Px int); method public void setProgress(int); method public void setProgress(int, boolean); method public void setProgressBackgroundTintList(@Nullable android.content.res.ColorStateList); @@ -56654,6 +56676,7 @@ package android.widget { method public boolean isFillViewport(); method public boolean isSmoothScrollingEnabled(); method public boolean pageScroll(int); + method public void scrollToDescendant(android.view.View); method public void setFillViewport(boolean); method public void setSmoothScrollingEnabled(boolean); method public final void smoothScrollBy(int, int); diff --git a/api/removed.txt b/api/removed.txt index 9f4b0416246d..f5bd434c3cc0 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -344,7 +344,7 @@ package android.os { public final class PowerManager { method public void goToSleep(long); method @Deprecated public void userActivity(long, boolean); - method public void wakeUp(long); + method @Deprecated public void wakeUp(long); } public class RecoverySystem { @@ -507,6 +507,19 @@ package android.provider { field @Deprecated public static final String TIMESTAMP = "timestamp"; } + public final class MediaStore { + 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); + method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); + method @Deprecated public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri); + } + + public static interface MediaStore.MediaColumns extends android.provider.BaseColumns { + field @Deprecated public static final String DATE_EXPIRES = "date_expires"; + field @Deprecated public static final String HASH = "_hash"; + field @Deprecated public static final String IS_TRASHED = "is_trashed"; + } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { field @Deprecated public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } diff --git a/api/system-current.txt b/api/system-current.txt index a0eef1d64bcc..c9b8c3867e59 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -201,7 +201,6 @@ package android { } public static final class R.array { - field public static final int config_defaultRoleHolders = 17235974; // 0x1070006 field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005 } @@ -237,6 +236,12 @@ package android { } public static final class R.string { + field public static final int config_defaultAssistant = 17039393; // 0x1040021 + field public static final int config_defaultBrowser = 17039394; // 0x1040022 + field public static final int config_defaultDialer = 17039395; // 0x1040023 + field public static final int config_defaultGallery = 17039398; // 0x1040026 + field public static final int config_defaultMusic = 17039397; // 0x1040025 + field public static final int config_defaultSms = 17039396; // 0x1040024 field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020 field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d @@ -711,6 +716,7 @@ package android.app.backup { method @Deprecated public int requestRestore(android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor); method @Deprecated @RequiresPermission(android.Manifest.permission.BACKUP) public String selectBackupTransport(String); method @RequiresPermission(android.Manifest.permission.BACKUP) public void selectBackupTransport(android.content.ComponentName, android.app.backup.SelectBackupTransportCallback); + method @RequiresPermission(android.Manifest.permission.BACKUP) public void setAncestralSerialNumber(long); method @RequiresPermission(android.Manifest.permission.BACKUP) public void setAutoRestore(boolean); method @RequiresPermission(android.Manifest.permission.BACKUP) public void setBackupEnabled(boolean); method @RequiresPermission(android.Manifest.permission.BACKUP) public void updateTransportAttributes(android.content.ComponentName, String, @Nullable android.content.Intent, String, @Nullable android.content.Intent, @Nullable String); @@ -1071,16 +1077,17 @@ package android.app.role { public final class RoleManager { method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); + field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1 field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; } @@ -3912,11 +3919,13 @@ package android.net { public class ConnectivityManager { method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); method public boolean getAvoidBadWifi(); method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl(); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); @@ -4999,10 +5008,11 @@ package android.os { method public boolean getEnableAdjustBrightness(); method public boolean getEnableDataSaver(); method public boolean getEnableFirewall(); + method public boolean getEnableNightMode(); method public boolean getEnableQuickDoze(); method public boolean getForceAllAppsStandby(); method public boolean getForceBackgroundCheck(); - method public int getGpsMode(); + method public int getLocationMode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.os.BatterySaverPolicyConfig> CREATOR; } @@ -5024,10 +5034,11 @@ package android.os { method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableAdjustBrightness(boolean); method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableDataSaver(boolean); method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableFirewall(boolean); + method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableNightMode(boolean); method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableQuickDoze(boolean); method @NonNull public android.os.BatterySaverPolicyConfig.Builder setForceAllAppsStandby(boolean); method @NonNull public android.os.BatterySaverPolicyConfig.Builder setForceBackgroundCheck(boolean); - method @NonNull public android.os.BatterySaverPolicyConfig.Builder setGpsMode(int); + method @NonNull public android.os.BatterySaverPolicyConfig.Builder setLocationMode(int); } public class Binder implements android.os.IBinder { @@ -5733,10 +5744,6 @@ package android.provider { field public static final String SERVICE_ENABLED = "service_enabled"; } - public static interface DeviceConfig.ContentCapture { - field public static final String NAMESPACE = "content_capture"; - } - public static interface DeviceConfig.DexBoot { field public static final String NAMESPACE = "dex_boot"; field public static final String PRIV_APPS_OOB_ENABLED = "priv_apps_oob_enabled"; @@ -5771,11 +5778,6 @@ package android.provider { field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; } - public static interface DeviceConfig.Runtime { - field public static final String NAMESPACE = "runtime"; - field public static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled"; - } - public static interface DeviceConfig.RuntimeNative { field public static final String NAMESPACE = "runtime_native"; } @@ -6035,11 +6037,11 @@ package android.rolecontrollerservice { public abstract class RoleControllerService extends android.app.Service { ctor public RoleControllerService(); - method public abstract void onAddRoleHolder(@NonNull String, @NonNull String, @NonNull android.app.role.RoleManagerCallback); + method public abstract void onAddRoleHolder(@NonNull String, @NonNull String, int, @NonNull android.app.role.RoleManagerCallback); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); - method public abstract void onClearRoleHolders(@NonNull String, @NonNull android.app.role.RoleManagerCallback); + method public abstract void onClearRoleHolders(@NonNull String, int, @NonNull android.app.role.RoleManagerCallback); method public abstract void onGrantDefaultRoles(@NonNull android.app.role.RoleManagerCallback); - method public abstract void onRemoveRoleHolder(@NonNull String, @NonNull String, @NonNull android.app.role.RoleManagerCallback); + method public abstract void onRemoveRoleHolder(@NonNull String, @NonNull String, int, @NonNull android.app.role.RoleManagerCallback); method public abstract void onSmsKillSwitchToggled(boolean); field public static final String SERVICE_INTERFACE = "android.rolecontrollerservice.RoleControllerService"; } diff --git a/api/test-current.txt b/api/test-current.txt index 49c4e6853212..b2ead4ab2464 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -17,8 +17,9 @@ package android { field public static final String WRITE_OBB = "android.permission.WRITE_OBB"; } - public static final class R.array { - field public static final int config_defaultRoleHolders = 17235974; // 0x1070006 + public static final class R.string { + field public static final int config_defaultAssistant = 17039393; // 0x1040021 + field public static final int config_defaultDialer = 17039395; // 0x1040023 } } @@ -429,11 +430,11 @@ package android.app.prediction { package android.app.role { public final class RoleManager { - method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); - method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); - method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; } @@ -767,6 +768,7 @@ package android.location { method public void setCodeType(int); method public void setConstellationType(int); method public void setMultipathIndicator(int); + method public void setOtherCodeTypeName(@NonNull String); method public void setPseudorangeRateMetersPerSecond(double); method public void setPseudorangeRateUncertaintyMetersPerSecond(double); method public void setReceivedSvTimeNanos(long); @@ -934,6 +936,7 @@ package android.net { } public class ConnectivityManager { + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; } @@ -1782,8 +1785,10 @@ package android.provider { } public final class DeviceConfig { + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(String, String); method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static void resetToDefaults(int, @Nullable String); method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static boolean setProperty(String, String, String, boolean); + field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; } public static interface DeviceConfig.Privacy { @@ -1851,6 +1856,8 @@ package android.provider { field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; + field public static final String NOTIFICATION_BADGING = "notification_badging"; + field public static final String NOTIFICATION_BUBBLES = "notification_bubbles"; field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; field public static final String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; @@ -2149,6 +2156,10 @@ package android.telecom { ctor public CallAudioState(boolean, int, int, @Nullable android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.bluetooth.BluetoothDevice>); } + public abstract class Conference extends android.telecom.Conferenceable { + method public android.telecom.Connection getPrimaryConnection(); + } + public final class PhoneAccountSuggestion implements android.os.Parcelable { ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean); } @@ -2161,6 +2172,16 @@ package android.telecom { field public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService"; } + public class TelecomManager { + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(android.telecom.PhoneAccountHandle); + field public static final int TTY_MODE_FULL = 1; // 0x1 + field public static final int TTY_MODE_HCO = 2; // 0x2 + field public static final int TTY_MODE_OFF = 0; // 0x0 + field public static final int TTY_MODE_VCO = 3; // 0x3 + } + } package android.telephony { @@ -2577,6 +2598,10 @@ package android.view { method public void setDisplayId(int); } + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod { + method public abstract String asyncImpl() default ""; + } + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { method public android.view.View getTooltipView(); method public static boolean isDefaultFocusHighlightEnabled(); @@ -2663,6 +2688,8 @@ package android.view.autofill { public final class AutofillId implements android.os.Parcelable { ctor public AutofillId(int); ctor public AutofillId(@NonNull android.view.autofill.AutofillId, int); + ctor public AutofillId(int, int); + ctor public AutofillId(@NonNull android.view.autofill.AutofillId, long, int); } public final class AutofillManager { @@ -2708,10 +2735,71 @@ package android.view.contentcapture { public final class ContentCaptureManager { method public boolean isContentCaptureFeatureEnabled(); method public void setContentCaptureFeatureEnabled(boolean); + field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled"; } public final class ViewNode extends android.app.assist.AssistStructure.ViewNode { method @Nullable public android.view.autofill.AutofillId getParentAutofillId(); + method @Nullable public static android.view.contentcapture.ViewNode readFromParcel(@NonNull android.os.Parcel); + method public static void writeToParcel(@NonNull android.os.Parcel, @Nullable android.view.contentcapture.ViewNode, int); + } + + public static final class ViewNode.ViewStructureImpl extends android.view.ViewStructure { + ctor public ViewNode.ViewStructureImpl(@NonNull android.view.View); + ctor public ViewNode.ViewStructureImpl(@NonNull android.view.autofill.AutofillId, long, int); + method public int addChildCount(int); + method public void asyncCommit(); + method public android.view.ViewStructure asyncNewChild(int); + method public android.view.autofill.AutofillId getAutofillId(); + method public int getChildCount(); + method public android.os.Bundle getExtras(); + method public CharSequence getHint(); + method public android.view.contentcapture.ViewNode getNode(); + method public android.graphics.Rect getTempRect(); + method public CharSequence getText(); + method public int getTextSelectionEnd(); + method public int getTextSelectionStart(); + method public boolean hasExtras(); + method public android.view.ViewStructure newChild(int); + method public android.view.ViewStructure.HtmlInfo.Builder newHtmlInfoBuilder(String); + method public void setAccessibilityFocused(boolean); + method public void setActivated(boolean); + method public void setAlpha(float); + method public void setAssistBlocked(boolean); + method public void setAutofillHints(String[]); + method public void setAutofillId(android.view.autofill.AutofillId); + method public void setAutofillId(android.view.autofill.AutofillId, int); + method public void setAutofillOptions(CharSequence[]); + method public void setAutofillType(int); + method public void setAutofillValue(android.view.autofill.AutofillValue); + method public void setCheckable(boolean); + method public void setChecked(boolean); + method public void setChildCount(int); + method public void setClassName(String); + method public void setClickable(boolean); + method public void setContentDescription(CharSequence); + method public void setContextClickable(boolean); + method public void setDataIsSensitive(boolean); + method public void setDimens(int, int, int, int, int, int); + method public void setElevation(float); + method public void setEnabled(boolean); + method public void setFocusable(boolean); + method public void setFocused(boolean); + method public void setHint(CharSequence); + method public void setHtmlInfo(android.view.ViewStructure.HtmlInfo); + method public void setId(int, String, String, String); + method public void setInputType(int); + method public void setLocaleList(android.os.LocaleList); + method public void setLongClickable(boolean); + method public void setOpaque(boolean); + method public void setSelected(boolean); + method public void setText(CharSequence); + method public void setText(CharSequence, int, int); + method public void setTextLines(int[], int[]); + method public void setTextStyle(float, int, int, int); + method public void setTransformation(android.graphics.Matrix); + method public void setVisibility(int); + method public void setWebDomain(String); } } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 5dcb392b002d..46917e4f6062 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -252,10 +252,12 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) status_t BootAnimation::readyToRun() { mAssets.addDefaultAssets(); - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); + if (mDisplayToken == nullptr) + return -1; + DisplayInfo dinfo; - status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); + status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo); if (status) return -1; @@ -1014,16 +1016,13 @@ void BootAnimation::handleViewport(nsecs_t timestep) { // At the end of the animation, we switch to the viewport that DisplayManager will apply // later. This changes the coordinate system, and means we must move the surface up by // the inset amount. - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); - Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset); Rect displayRect(0, mTargetInset, mWidth, mHeight); SurfaceComposerClient::Transaction t; t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset) .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight)); - t.setDisplayProjection(dtoken, 0 /* orientation */, layerStackRect, displayRect); + t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect); t.apply(); mTargetInset = mCurrentInset = 0; diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 04d4f9a6fd06..19616cb790c7 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -171,6 +171,7 @@ private: EGLDisplay mDisplay; EGLDisplay mContext; EGLDisplay mSurface; + sp<IBinder> mDisplayToken; sp<SurfaceControl> mFlingerSurfaceControl; sp<Surface> mFlingerSurface; bool mClockEnabled; diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 056add5c71b5..d757e4611158 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -43,6 +43,7 @@ cc_library { "libidmap2/PrettyPrintVisitor.cpp", "libidmap2/RawPrintVisitor.cpp", "libidmap2/ResourceUtils.cpp", + "libidmap2/Result.cpp", "libidmap2/Xml.cpp", "libidmap2/ZipFile.cpp", ], @@ -55,6 +56,7 @@ cc_library { shared_libs: [ "libandroidfw", "libbase", + "libcutils", "libutils", "libziparchive", ], @@ -93,6 +95,7 @@ cc_test { "tests/PrettyPrintVisitorTests.cpp", "tests/RawPrintVisitorTests.cpp", "tests/ResourceUtilsTests.cpp", + "tests/ResultTests.cpp", "tests/XmlTests.cpp", "tests/ZipFileTests.cpp", ], @@ -148,6 +151,7 @@ cc_binary { shared_libs: [ "libandroidfw", "libbase", + "libcutils", "libidmap2", "libutils", "libziparchive", diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index 0c581f3b1a98..6703909d887e 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -29,6 +29,7 @@ #include "idmap2/Idmap.h" #include "idmap2/Policies.h" #include "idmap2/Result.h" +#include "idmap2/SysTrace.h" using android::ApkAssets; using android::idmap2::BinaryStreamVisitor; @@ -42,6 +43,7 @@ using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::UidHasWriteAccessToPath; bool Create(const std::vector<std::string>& args, std::ostream& out_error) { + SYSTRACE << "Create " << args; std::string target_apk_path; std::string overlay_apk_path; std::string idmap_path; diff --git a/cmds/idmap2/idmap2/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp index c8cdcfa6d3dc..3947703fe8da 100644 --- a/cmds/idmap2/idmap2/Dump.cpp +++ b/cmds/idmap2/idmap2/Dump.cpp @@ -24,6 +24,7 @@ #include "idmap2/Idmap.h" #include "idmap2/PrettyPrintVisitor.h" #include "idmap2/RawPrintVisitor.h" +#include "idmap2/SysTrace.h" using android::idmap2::CommandLineOptions; using android::idmap2::Idmap; @@ -31,6 +32,7 @@ using android::idmap2::PrettyPrintVisitor; using android::idmap2::RawPrintVisitor; bool Dump(const std::vector<std::string>& args, std::ostream& out_error) { + SYSTRACE << "Dump " << args; std::string idmap_path; bool verbose; diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index cfb5dd5b30a9..553d8cac99e4 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -37,6 +37,7 @@ #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" #include "idmap2/Result.h" +#include "idmap2/SysTrace.h" #include "idmap2/Xml.h" #include "idmap2/ZipFile.h" @@ -156,6 +157,7 @@ Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path } // namespace bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) { + SYSTRACE << "Lookup " << args; std::vector<std::string> idmap_paths; std::string config_str; std::string resid_str; diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp index 445fac52f997..a0ffccb26dfe 100644 --- a/cmds/idmap2/idmap2/Main.cpp +++ b/cmds/idmap2/idmap2/Main.cpp @@ -24,6 +24,7 @@ #include <vector> #include "idmap2/CommandLineOptions.h" +#include "idmap2/SysTrace.h" #include "Commands.h" @@ -48,6 +49,7 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { } // namespace int main(int argc, char** argv) { + SYSTRACE << "main"; const NameToFunctionMap commands = { {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify}, }; diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index b1ed42a3e624..873779f386f5 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -30,6 +30,7 @@ #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" #include "idmap2/ResourceUtils.h" +#include "idmap2/SysTrace.h" #include "idmap2/Xml.h" #include "idmap2/ZipFile.h" @@ -67,6 +68,7 @@ bool VendorIsQOrLater() { std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs, bool recursive, std::ostream& out_error) { + SYSTRACE << "FindApkFiles " << dirs << " " << recursive; const auto predicate = [](unsigned char type, const std::string& path) -> bool { static constexpr size_t kExtLen = 4; // strlen(".apk") return type == DT_REG && path.size() > kExtLen && @@ -104,6 +106,7 @@ PolicyBitmask PolicyForPath(const std::string& apk_path) { } // namespace bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { + SYSTRACE << "Scan " << args; std::vector<std::string> input_directories; std::string target_package_name; std::string target_apk_path; diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp index 4d4a0e769174..d8fe7aa0ed99 100644 --- a/cmds/idmap2/idmap2/Verify.cpp +++ b/cmds/idmap2/idmap2/Verify.cpp @@ -21,11 +21,13 @@ #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" +#include "idmap2/SysTrace.h" using android::idmap2::CommandLineOptions; using android::idmap2::IdmapHeader; bool Verify(const std::vector<std::string>& args, std::ostream& out_error) { + SYSTRACE << "Verify " << args; std::string idmap_path; const CommandLineOptions opts = diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index f30ce9b08d6e..0e4bd89e355c 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -29,13 +29,13 @@ #include "android-base/stringprintf.h" #include "binder/IPCThreadState.h" #include "utils/String8.h" -#include "utils/Trace.h" #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" #include "idmap2/Policies.h" #include "idmap2/Result.h" +#include "idmap2/SysTrace.h" #include "idmap2d/Idmap2Service.h" @@ -72,6 +72,7 @@ namespace android::os { Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path, int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) { assert(_aidl_return); + SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_apk_path; *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); return ok(); } @@ -79,6 +80,7 @@ Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path, Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { assert(_aidl_return); + SYSTRACE << "Idmap2Service::removeIdmap " << overlay_apk_path; const uid_t uid = IPCThreadState::self()->getCallingUid(); const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); if (!UidHasWriteAccessToPath(uid, idmap_path)) { @@ -98,6 +100,7 @@ Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies ATTRIBUTE_UNUSED, bool enforce_overlayable ATTRIBUTE_UNUSED, int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { + SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path; assert(_aidl_return); const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); std::ifstream fin(idmap_path); @@ -113,15 +116,10 @@ Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, Status Idmap2Service::createIdmap(const std::string& target_apk_path, const std::string& overlay_apk_path, int32_t fulfilled_policies, - bool enforce_overlayable, int32_t user_id, + bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, std::unique_ptr<std::string>* _aidl_return) { assert(_aidl_return); - std::stringstream trace; - trace << __FUNCTION__ << " " << target_apk_path << " " << overlay_apk_path << " " - << std::to_string(user_id); - ATRACE_NAME(trace.str().c_str()); - std::cout << trace.str() << std::endl; - + SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path; _aidl_return->reset(nullptr); const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies); diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h index 6189ea371ee1..d88dd5179610 100644 --- a/cmds/idmap2/include/idmap2/Result.h +++ b/cmds/idmap2/include/idmap2/Result.h @@ -18,6 +18,11 @@ #define IDMAP2_INCLUDE_IDMAP2_RESULT_H_ #include <optional> +#include <string> +#include <utility> +#include <variant> + +#include "android-base/logging.h" // CHECK namespace android::idmap2 { @@ -26,6 +31,125 @@ using Result = std::optional<T>; static constexpr std::nullopt_t kResultError = std::nullopt; +namespace v2 { + +using Unit = std::monostate; + +class Error { + public: + explicit Error(const Error& parent) = default; + + // NOLINTNEXTLINE(cert-dcl50-cpp) + explicit Error(const char* fmt, ...) __attribute__((__format__(printf, 2, 3))); + + // NOLINTNEXTLINE(cert-dcl50-cpp) + explicit Error(const Error& parent, const char* fmt, ...) + __attribute__((__format__(printf, 3, 4))); + + inline std::string GetMessage() const { + return msg_; + } + + private: + std::string msg_; +}; + +template <typename T> +class Result { + public: + Result(const T& value); // NOLINT(runtime/explicit) + Result(T&& value) noexcept; // NOLINT(runtime/explicit) + + Result(const Error& error); // NOLINT(runtime/explicit) + Result(Error&& error) noexcept; // NOLINT(runtime/explicit) + + Result(const Result& error) = default; + + Result& operator=(const Result& rhs) = default; + Result& operator=(Result&& rhs) noexcept = default; + + explicit operator bool() const; + + constexpr const T& operator*() const&; + T& operator*() &; + + constexpr const T* operator->() const&; + T* operator->() &; + + std::string GetErrorMessage() const; + Error GetError() const; + + private: + bool is_ok() const; + + std::variant<T, Error> data_; +}; + +template <typename T> +Result<T>::Result(const T& value) : data_(std::in_place_type<T>, value) { +} + +template <typename T> +Result<T>::Result(T&& value) noexcept : data_(std::in_place_type<T>, std::forward<T>(value)) { +} + +template <typename T> +Result<T>::Result(const Error& error) : data_(std::in_place_type<Error>, error) { +} + +template <typename T> +Result<T>::Result(Error&& error) noexcept + : data_(std::in_place_type<Error>, std::forward<Error>(error)) { +} + +template <typename T> +Result<T>::operator bool() const { + return is_ok(); +} + +template <typename T> +constexpr const T& Result<T>::operator*() const& { + CHECK(is_ok()) << "Result<T>::operator* called in ERROR state"; + return std::get<T>(data_); +} + +template <typename T> +T& Result<T>::operator*() & { + CHECK(is_ok()) << "Result<T>::operator* called in ERROR state"; + return std::get<T>(data_); +} + +template <typename T> +constexpr const T* Result<T>::operator->() const& { + CHECK(is_ok()) << "Result<T>::operator-> called in ERROR state"; + return &std::get<T>(data_); +} + +template <typename T> +T* Result<T>::operator->() & { + CHECK(is_ok()) << "Result<T>::operator-> called in ERROR state"; + return &std::get<T>(data_); +} + +template <typename T> +inline std::string Result<T>::GetErrorMessage() const { + CHECK(!is_ok()) << "Result<T>::GetErrorMessage called in OK state"; + return std::get<Error>(data_).GetMessage(); +} + +template <typename T> +inline Error Result<T>::GetError() const { + CHECK(!is_ok()) << "Result<T>::GetError called in OK state"; + return Error(std::get<Error>(data_)); +} + +template <typename T> +inline bool Result<T>::is_ok() const { + return std::holds_alternative<T>(data_); +} + +} // namespace v2 + } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_ diff --git a/cmds/idmap2/include/idmap2/SysTrace.h b/cmds/idmap2/include/idmap2/SysTrace.h new file mode 100644 index 000000000000..19b4353def18 --- /dev/null +++ b/cmds/idmap2/include/idmap2/SysTrace.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef IDMAP2_INCLUDE_IDMAP2_SYSTRACE_H_ +#define IDMAP2_INCLUDE_IDMAP2_SYSTRACE_H_ + +#define ATRACE_TAG ATRACE_TAG_RRO + +#include <sstream> +#include <vector> + +#include "cutils/trace.h" + +namespace android::idmap2::utils { +#ifdef __ANDROID__ + +class ScopedTraceNoStart { + public: + ~ScopedTraceNoStart() { + ATRACE_END(); + } +}; + +class ScopedTraceMessageHelper { + public: + ~ScopedTraceMessageHelper() { + ATRACE_BEGIN(buffer_.str().c_str()); + } + + std::ostream& stream() { + return buffer_; + } + + private: + std::ostringstream buffer_; +}; + +#define SYSTRACE \ + android::idmap2::utils::ScopedTraceNoStart _trace##__LINE__; \ + (ATRACE_ENABLED()) && android::idmap2::utils::ScopedTraceMessageHelper().stream() + +#else + +class DummyStream { + public: + std::ostream& stream() { + return buffer_; + } + + private: + std::ostringstream buffer_; +}; + +#define SYSTRACE android::idmap2::utils::DummyStream().stream() + +#endif +} // namespace android::idmap2::utils + +template <typename T> +std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vector) { + bool first = true; + stream << "["; + for (const auto& item : vector) { + if (!first) { + stream << ", "; + } + stream << item; + first = false; + } + stream << "]"; + return stream; +} + +#endif // IDMAP2_INCLUDE_IDMAP2_SYSTRACE_H_ diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index fa5ac8ea11dc..99b5f0ff3c2d 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -34,6 +34,7 @@ #include "idmap2/Idmap.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" +#include "idmap2/SysTrace.h" #include "idmap2/ZipFile.h" namespace android::idmap2 { @@ -117,6 +118,12 @@ const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { return loaded_arsc.GetPackageById(id); } +Result<uint32_t> GetCrc(const ZipFile& zip) { + const Result<uint32_t> a = zip.Crc("resources.arsc"); + const Result<uint32_t> b = zip.Crc("AndroidManifest.xml"); + return a && b ? Result<uint32_t>(*a ^ *b) : kResultError; +} + } // namespace std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { @@ -153,7 +160,7 @@ bool IdmapHeader::IsUpToDate(std::ostream& out_error) const { return false; } - Result<uint32_t> target_crc = target_zip->Crc("resources.arsc"); + Result<uint32_t> target_crc = GetCrc(*target_zip); if (!target_crc) { out_error << "error: failed to get target crc" << std::endl; return false; @@ -173,7 +180,7 @@ bool IdmapHeader::IsUpToDate(std::ostream& out_error) const { return false; } - Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc"); + Result<uint32_t> overlay_crc = GetCrc(*overlay_zip); if (!overlay_crc) { out_error << "error: failed to get overlay crc" << std::endl; return false; @@ -252,6 +259,7 @@ std::string Idmap::CanonicalIdmapPathFor(const std::string& absolute_dir, std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream, std::ostream& out_error) { + SYSTRACE << "Idmap::FromBinaryStream"; std::unique_ptr<Idmap> idmap(new Idmap()); idmap->header_ = IdmapHeader::FromBinaryStream(stream); @@ -298,6 +306,7 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets( const std::string& target_apk_path, const ApkAssets& target_apk_assets, const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error) { + SYSTRACE << "Idmap::FromApkAssets"; AssetManager2 target_asset_manager; if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) { out_error << "error: failed to create target asset manager" << std::endl; @@ -356,14 +365,14 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets( header->magic_ = kIdmapMagic; header->version_ = kIdmapCurrentVersion; - Result<uint32_t> crc = target_zip->Crc("resources.arsc"); + Result<uint32_t> crc = GetCrc(*target_zip); if (!crc) { out_error << "error: failed to get zip crc for target" << std::endl; return nullptr; } header->target_crc_ = *crc; - crc = overlay_zip->Crc("resources.arsc"); + crc = GetCrc(*overlay_zip); if (!crc) { out_error << "error: failed to get zip crc for overlay" << std::endl; return nullptr; diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.java b/cmds/idmap2/libidmap2/Result.cpp index 371b8b723d0a..a5c999916c72 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantEvent.java +++ b/cmds/idmap2/libidmap2/Result.cpp @@ -13,13 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.telephony.ims; -import android.os.Parcelable; +#include <cstdarg> -/** - * An event that is associated with an {@link RcsParticipant} - * @hide - TODO(sahinc) make this public - */ -public abstract class RcsParticipantEvent implements Parcelable { +#include "android-base/stringprintf.h" + +#include "idmap2/Result.h" + +namespace android::idmap2 { + +v2::Error::Error(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + base::StringAppendV(&msg_, fmt, ap); + va_end(ap); } + +v2::Error::Error(const Error& parent, const char* fmt, ...) : msg_(parent.msg_) { + msg_.append(" -> "); + + va_list ap; + va_start(ap, fmt); + base::StringAppendV(&msg_, fmt, ap); + va_end(ap); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh index ad9830b8a096..41d3c69540b2 100755 --- a/cmds/idmap2/static-checks.sh +++ b/cmds/idmap2/static-checks.sh @@ -59,7 +59,7 @@ function _clang_format() function _bpfmt() { - local output="$(bpfmt -s -d $bp_files)" + local output="$(bpfmt -d $bp_files)" if [[ "$output" ]]; then echo "$output" return 1 @@ -72,7 +72,9 @@ function _cpplint() local cpplint="${ANDROID_BUILD_TOP}/tools/repohooks/tools/cpplint.py" local output="$($cpplint --quiet $cpp_files 2>&1 >/dev/null | grep -v \ -e 'Found C system header after C++ system header.' \ + -e 'Unknown NOLINT error category: cert-dcl50-cpp' \ -e 'Unknown NOLINT error category: misc-non-private-member-variables-in-classes' \ + -e 'Unknown NOLINT error category: performance-unnecessary-copy-initialization' \ )" if [[ "$output" ]]; then echo "$output" @@ -115,7 +117,7 @@ if [[ $opt_mode == "check" ]]; then exit $errors elif [[ $opt_mode == "fix" ]]; then clang-format -style=file -i $cpp_files - bpfmt -s -w $bp_files + bpfmt -w $bp_files exit 0 elif [[ $opt_mode == "help" ]]; then echo "Run static analysis tools such as clang-format and cpplint on the idmap2" diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 9e27ccd7cfbb..b40521f054af 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -191,8 +191,8 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); - ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xab7cf70d); - ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b); + ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xdd53ca29); + ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xa71ccd77); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index b1ca12557dcb..a5588c330d85 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -52,8 +52,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000008: ab7cf70d target crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000000c: d470336b overlay crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000008: dd53ca29 target crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000000c: a71ccd77 overlay crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos); } diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp new file mode 100644 index 000000000000..d82f0c475ea9 --- /dev/null +++ b/cmds/idmap2/tests/ResultTests.cpp @@ -0,0 +1,288 @@ +/* + * 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 <memory> +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "idmap2/Result.h" + +namespace android::idmap2 { + +struct Container { + uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +// Tests: Error + +TEST(ResultTests, ErrorTraits) { + ASSERT_TRUE(std::is_move_constructible<v2::Error>::value); + ASSERT_TRUE(std::is_move_assignable<v2::Error>::value); + ASSERT_TRUE(std::is_copy_constructible<v2::Error>::value); + ASSERT_TRUE(std::is_copy_assignable<v2::Error>::value); +} + +TEST(ResultTests, ErrorCtorFormat) { + v2::Error e("%s=0x%08x", "resid", 0x7f010002); + ASSERT_EQ(e.GetMessage(), "resid=0x7f010002"); +} + +TEST(ResultTests, ErrorPropagateParent) { + v2::Error e1("foo"); + ASSERT_EQ(e1.GetMessage(), "foo"); + + v2::Error e2(e1, "bar"); + ASSERT_EQ(e2.GetMessage(), "foo -> bar"); + + v2::Error e3(e2); // NOLINT(performance-unnecessary-copy-initialization) + ASSERT_EQ(e3.GetMessage(), "foo -> bar"); + + v2::Error e4(e3, "%02d", 1); + ASSERT_EQ(e4.GetMessage(), "foo -> bar -> 01"); +} + +// Tests: Result<T> member functions + +// Result(const Result&) +TEST(ResultTests, CopyConstructor) { + v2::Result<uint32_t> r1(42U); + + v2::Result<uint32_t> r2(r1); + ASSERT_TRUE(r2); + ASSERT_EQ(*r2, 42U); + + v2::Result<uint32_t> r3 = r2; + ASSERT_TRUE(r3); + ASSERT_EQ(*r3, 42U); +} + +// Result(const T&) +TEST(ResultTests, Constructor) { + uint32_t v = 42U; + v2::Result<uint32_t> r1(v); + ASSERT_TRUE(r1); + ASSERT_EQ(*r1, 42U); + + v2::Error e("foo"); + v2::Result<uint32_t> r2(e); + ASSERT_FALSE(r2); + ASSERT_EQ(r2.GetErrorMessage(), "foo"); +} + +// Result(const T&&) +TEST(ResultTests, MoveConstructor) { + v2::Result<uint32_t> r1(42U); + ASSERT_TRUE(r1); + ASSERT_EQ(*r1, 42U); + + v2::Result<uint32_t> r2(v2::Error("foo")); + ASSERT_FALSE(r2); + ASSERT_EQ(r2.GetErrorMessage(), "foo"); +} + +// operator= +TEST(ResultTests, CopyAssignmentOperator) { + // note: 'Result<...> r2 = r1;' calls the copy ctor + v2::Result<uint32_t> r1(42U); + v2::Result<uint32_t> r2(0U); + r2 = r1; + ASSERT_TRUE(r2); + ASSERT_EQ(*r2, 42U); + + v2::Result<uint32_t> r3(v2::Error("foo")); + r2 = r3; + ASSERT_FALSE(r2); + ASSERT_EQ(r2.GetErrorMessage(), "foo"); +} + +TEST(ResultTests, MoveAssignmentOperator) { + v2::Result<uint32_t> r(0U); + r = v2::Result<uint32_t>(42U); + ASSERT_TRUE(r); + ASSERT_EQ(*r, 42U); + + r = v2::Result<uint32_t>(v2::Error("foo")); + ASSERT_FALSE(r); + ASSERT_EQ(r.GetErrorMessage(), "foo"); +} + +// operator bool() +TEST(ResultTests, BoolOperator) { + v2::Result<uint32_t> r1(42U); + ASSERT_TRUE(r1); + ASSERT_EQ(*r1, 42U); + + v2::Result<uint32_t> r2(v2::Error("foo")); + ASSERT_FALSE(r2); + ASSERT_EQ(r2.GetErrorMessage(), "foo"); +} + +// operator* +TEST(ResultTests, IndirectionOperator) { + const v2::Result<uint32_t> r1(42U); + ASSERT_TRUE(r1); + ASSERT_EQ(*r1, 42U); + + const v2::Result<Container> r2(Container{42U}); + ASSERT_TRUE(r2); + const Container& c = *r2; + ASSERT_EQ(c.value, 42U); + + v2::Result<Container> r3(Container{42U}); + ASSERT_TRUE(r3); + ASSERT_EQ((*r3).value, 42U); + (*r3).value = 0U; + ASSERT_EQ((*r3).value, 0U); +} + +// operator-> +TEST(ResultTests, DereferenceOperator) { + const v2::Result<Container> r1(Container{42U}); + ASSERT_TRUE(r1); + ASSERT_EQ(r1->value, 42U); + + v2::Result<Container> r2(Container{42U}); + ASSERT_TRUE(r2); + ASSERT_EQ(r2->value, 42U); + r2->value = 0U; + ASSERT_EQ(r2->value, 0U); +} + +// Tests: intended use of Result<T> + +TEST(ResultTests, ResultTraits) { + ASSERT_TRUE(std::is_move_constructible<v2::Result<uint32_t>>::value); + ASSERT_TRUE(std::is_move_assignable<v2::Result<uint32_t>>::value); + ASSERT_TRUE(std::is_copy_constructible<v2::Result<uint32_t>>::value); + ASSERT_TRUE(std::is_copy_assignable<v2::Result<uint32_t>>::value); +} + +TEST(ResultTests, UnitTypeResult) { + v2::Result<v2::Unit> r(v2::Unit{}); + ASSERT_TRUE(r); +} + +struct RefCountData { + int ctor; // NOLINT(misc-non-private-member-variables-in-classes) + int copy_ctor; // NOLINT(misc-non-private-member-variables-in-classes) + int dtor; // NOLINT(misc-non-private-member-variables-in-classes) + int move; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +class RefCountContainer { + public: + explicit RefCountContainer(RefCountData& data) : data_(data) { + ++data_.ctor; + } + + RefCountContainer(RefCountContainer const&) = delete; + + RefCountContainer(RefCountContainer&& rhs) noexcept : data_(rhs.data_) { + ++data_.copy_ctor; + } + + RefCountContainer& operator=(RefCountContainer const&) = delete; + + RefCountContainer& operator=(RefCountContainer&& rhs) noexcept { + data_ = rhs.data_; + ++data_.move; + return *this; + } + + ~RefCountContainer() { + ++data_.dtor; + } + + private: + RefCountData& data_; +}; + +TEST(ResultTests, ReferenceCount) { + ASSERT_TRUE(std::is_move_constructible<RefCountContainer>::value); + ASSERT_TRUE(std::is_move_assignable<RefCountContainer>::value); + ASSERT_FALSE(std::is_copy_constructible<RefCountContainer>::value); + ASSERT_FALSE(std::is_copy_assignable<RefCountContainer>::value); + + RefCountData rc{0, 0, 0, 0}; + { v2::Result<RefCountContainer> r(RefCountContainer{rc}); } + ASSERT_EQ(rc.ctor, 1); + ASSERT_EQ(rc.copy_ctor, 1); + ASSERT_EQ(rc.move, 0); + ASSERT_EQ(rc.dtor, 2); +} + +v2::Result<Container> CreateContainer(bool succeed) { + if (!succeed) { + return v2::Error("foo"); + } + return Container{42U}; +} + +TEST(ResultTests, FunctionReturn) { + auto r1 = CreateContainer(true); + ASSERT_TRUE(r1); + ASSERT_EQ(r1->value, 42U); + + auto r2 = CreateContainer(false); + ASSERT_FALSE(r2); + ASSERT_EQ(r2.GetErrorMessage(), "foo"); + ASSERT_EQ(r2.GetError().GetMessage(), "foo"); +} + +v2::Result<Container> FailToCreateContainer() { + auto container = CreateContainer(false); + if (!container) { + return v2::Error(container.GetError(), "bar"); + } + return container; +} + +TEST(ResultTests, CascadeError) { + auto container = FailToCreateContainer(); + ASSERT_FALSE(container); + ASSERT_EQ(container.GetErrorMessage(), "foo -> bar"); +} + +struct NoCopyContainer { + uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes) + DISALLOW_COPY_AND_ASSIGN(NoCopyContainer); +}; + +v2::Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) { + if (!succeed) { + return v2::Error("foo"); + } + std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U}); + p->value = 42U; + return std::move(p); +} + +TEST(ResultTests, UniquePtr) { + auto r1 = CreateNoCopyContainer(true); + ASSERT_TRUE(r1); + ASSERT_EQ((*r1)->value, 42U); + (*r1)->value = 0U; + ASSERT_EQ((*r1)->value, 0U); + + auto r2 = CreateNoCopyContainer(false); + ASSERT_FALSE(r2); + ASSERT_EQ(r2.GetErrorMessage(), "foo"); +} + +} // namespace android::idmap2 diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp index 40da583e4f6c..3dc10939fed7 100644 --- a/cmds/incidentd/Android.bp +++ b/cmds/incidentd/Android.bp @@ -94,8 +94,10 @@ cc_test { data: ["testdata/**/*"], - static_libs: ["libgmock"], - + static_libs: [ + "libgmock", + "libplatformprotos", + ], shared_libs: [ "libbase", "libbinder", diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index f54f738fb62b..b5e41d767295 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -35,6 +35,16 @@ using namespace std; using ::testing::StrEq; using ::testing::Test; +namespace { +void getHeaderData(const IncidentHeaderProto& headerProto, vector<uint8_t>* out) { + out->clear(); + auto serialized = headerProto.SerializeAsString(); + if (serialized.empty()) return; + out->resize(serialized.length()); + std::copy(serialized.begin(), serialized.end(), out->begin()); +} +} + class TestListener : public IIncidentReportStatusListener { public: int startInvoked; @@ -143,7 +153,10 @@ TEST_F(ReporterTest, RunReportWithHeaders) { args2.addSection(2); IncidentHeaderProto header; header.set_alert_id(12); - args2.addHeader(header); + + vector<uint8_t> out; + getHeaderData(header, &out); + args2.addHeader(out); sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd); sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd); @@ -169,8 +182,12 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { IncidentHeaderProto header1, header2; header1.set_alert_id(12); header2.set_reason("abcd"); - args.addHeader(header1); - args.addHeader(header2); + + vector<uint8_t> out; + getHeaderData(header1, &out); + args.addHeader(out); + getHeaderData(header2, &out); + args.addHeader(out); sp<ReportRequest> r = new ReportRequest(args, l, -1); reporter->batch.add(r); diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 9b684a060286..24454ed15d40 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -82,6 +82,16 @@ protected: virtual IBinder* onAsBinder() override { return nullptr; }; }; +namespace { +void getHeaderData(const IncidentHeaderProto& headerProto, vector<uint8_t>* out) { + out->clear(); + auto serialized = headerProto.SerializeAsString(); + if (serialized.empty()) return; + out->resize(serialized.length()); + std::copy(serialized.begin(), serialized.end(), out->begin()); +} +} + TEST_F(SectionTest, HeaderSection) { HeaderSection hs; @@ -94,9 +104,15 @@ TEST_F(SectionTest, HeaderSection) { head1.set_reason("axe"); head2.set_reason("pup"); - args1.addHeader(head1); - args1.addHeader(head2); - args2.addHeader(head2); + vector<uint8_t> out; + getHeaderData(head1, &out); + args1.addHeader(out); + + getHeaderData(head2, &out); + args1.addHeader(out); + + getHeaderData(head2, &out); + args2.addHeader(out); requests.add(new ReportRequest(args1, new SimpleListener(), -1)); requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd)); diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 3d74f8b207af..c4976675dc04 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -46,23 +46,22 @@ using namespace android; -static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain; - #define COLORSPACE_UNKNOWN 0 #define COLORSPACE_SRGB 1 #define COLORSPACE_DISPLAY_P3 2 -static void usage(const char* pname) +static void usage(const char* pname, PhysicalDisplayId displayId) { fprintf(stderr, "usage: %s [-hp] [-d display-id] [FILENAME]\n" " -h: this message\n" " -p: save the file as a png.\n" - " -d: specify the display id to capture, default %d.\n" + " -d: specify the physical display ID to capture (default: %" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n" + " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n" "If FILENAME ends with .png it will be saved as a png.\n" "If FILENAME is not given, the results will be printed to stdout.\n", - pname, DEFAULT_DISPLAY_ID - ); + pname, displayId); } static SkColorType flinger2skia(PixelFormat f) @@ -127,9 +126,14 @@ static status_t notifyMediaScanner(const char* fileName) { int main(int argc, char** argv) { + std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId(); + if (!displayId) { + fprintf(stderr, "Failed to get token for internal display\n"); + return 1; + } + const char* pname = argv[0]; bool png = false; - int32_t displayId = DEFAULT_DISPLAY_ID; int c; while ((c = getopt(argc, argv, "phd:")) != -1) { switch (c) { @@ -137,11 +141,11 @@ int main(int argc, char** argv) png = true; break; case 'd': - displayId = atoi(optarg); + displayId = atoll(optarg); break; case '?': case 'h': - usage(pname); + usage(pname, *displayId); return 1; } } @@ -166,7 +170,7 @@ int main(int argc, char** argv) } if (fd == -1) { - usage(pname); + usage(pname, *displayId); return 1; } @@ -192,9 +196,10 @@ int main(int argc, char** argv) ProcessState::self()->setThreadPoolMaxThreadCount(0); ProcessState::self()->startThreadPool(); - sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId); - if (display == NULL) { - fprintf(stderr, "Unable to get handle for display %d\n", displayId); + const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(*displayId); + if (display == nullptr) { + fprintf(stderr, "Failed to get token for invalid display %" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT "\n", *displayId); return 1; } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index ca104823b7cf..d6f045ea43fd 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -50,6 +50,7 @@ cc_defaults { srcs: [ ":statsd_aidl", + "src/active_config_list.proto", "src/statsd_config.proto", "src/FieldValue.cpp", "src/hash.cpp", @@ -214,7 +215,7 @@ cc_test { "tests/anomaly/AnomalyTracker_test.cpp", "tests/ConfigManager_test.cpp", "tests/external/puller_util_test.cpp", - "tests/external/StatsPuller_test.cpp", + "tests/external/StatsPuller_test.cpp", "tests/indexed_priority_queue_test.cpp", "tests/LogEntryMatcher_test.cpp", "tests/LogEvent_test.cpp", diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 69cb264ef237..dd18bd4cc8ad 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -14,18 +14,19 @@ * limitations under the License. */ -#define DEBUG false // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "statslog.h" #include <android-base/file.h> #include <dirent.h> +#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> #include "StatsLogProcessor.h" -#include "stats_log_util.h" #include "android-base/stringprintf.h" +#include "external/StatsPullerManager.h" #include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" -#include "external/StatsPullerManager.h" +#include "stats_log_util.h" #include "stats_util.h" #include "storage/StorageManager.h" @@ -67,9 +68,17 @@ const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6; const int FIELD_ID_DUMP_REPORT_REASON = 8; const int FIELD_ID_STRINGS = 9; +const int FIELD_ID_ACTIVE_CONFIG_LIST = 1; +const int FIELD_ID_CONFIG_ID = 1; +const int FIELD_ID_CONFIG_UID = 2; +const int FIELD_ID_ACTIVE_METRIC = 3; +const int FIELD_ID_METRIC_ID = 1; +const int FIELD_ID_TIME_TO_LIVE_NANOS = 2; + #define NS_PER_HOUR 3600 * NS_PER_SEC #define STATS_DATA_DIR "/data/misc/stats-data" +#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric" // Cool down period for writing data to disk to avoid overwriting files. #define WRITE_DATA_COOL_DOWN_SEC 5 @@ -507,6 +516,71 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, mOnDiskDataConfigs.insert(key); } +void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + ProtoOutputStream proto; + + for (const auto& pair : mMetricsManagers) { + uint64_t activeConfigListToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_CONFIG_LIST); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_ID, (long long)pair.first.GetId()); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_UID, pair.first.GetUid()); + + vector<MetricProducer*> activeMetrics; + pair.second->prepForShutDown(currentTimeNs); + pair.second->getActiveMetrics(activeMetrics); + for (MetricProducer* metric : activeMetrics) { + if (metric->isActive()) { + uint64_t metricToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_METRIC); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, + (long long)metric->getMetricId()); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_TIME_TO_LIVE_NANOS, + (long long)metric->getRemainingTtlNs(currentTimeNs)); + proto.end(metricToken); + } + } + proto.end(activeConfigListToken); + } + + string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); + StorageManager::deleteFile(file_name.c_str()); + android::base::unique_fd fd( + open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); + if (fd == -1) { + ALOGE("Attempt to write %s but failed", file_name.c_str()); + return; + } + proto.flush(fd.get()); +} + +void StatsLogProcessor::LoadMetricsActivationFromDisk() { + string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); + int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); + if (fd != -1) { + string content; + if (android::base::ReadFdToString(fd, &content)) { + ActiveConfigList activeConfigList; + if (activeConfigList.ParseFromString(content)) { + for (int i = 0; i < activeConfigList.active_config_size(); i++) { + const auto& config = activeConfigList.active_config(i); + ConfigKey key(config.uid(), config.config_id()); + auto it = mMetricsManagers.find(key); + if (it == mMetricsManagers.end()) { + ALOGE("No config found for config %s", key.ToString().c_str()); + continue; + } + VLOG("Setting active config %s", key.ToString().c_str()); + it->second->setActiveMetrics(config, mTimeBaseNs); + } + } + VLOG("Successfully loaded %d active configs.", activeConfigList.active_config_size()); + } + close(fd); + } + StorageManager::deleteFile(file_name.c_str()); +} + void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason) { const int64_t timeNs = getElapsedRealtimeNs(); // Do not write to disk if we already have in the last few seconds. diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index a5ce9b65f899..caf1a713986d 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -80,6 +80,12 @@ public: /* Flushes data to disk. Data on memory will be gone after written to disk. */ void WriteDataToDisk(const DumpReportReason dumpReportReason); + /* Persist metric activation status onto disk. */ + void WriteMetricsActivationToDisk(int64_t currentTimeNs); + + /* Load metric activation status from disk. */ + void LoadMetricsActivationFromDisk(); + // Reset all configs. void resetConfigs(); @@ -188,6 +194,9 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); + FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index b26c713877db..86bf3eca3ee6 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -161,7 +161,8 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) mConfigManager = new ConfigManager(); mProcessor = new StatsLogProcessor( mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor, - getElapsedRealtimeNs(), [this](const ConfigKey& key) { + getElapsedRealtimeNs(), + [this](const ConfigKey& key) { sp<IStatsCompanionService> sc = getStatsCompanionService(); auto receiver = mConfigManager->GetConfigReceiver(key); if (sc == nullptr) { @@ -867,6 +868,7 @@ Status StatsService::informDeviceShutdown() { ENFORCE_UID(AID_SYSTEM); VLOG("StatsService::informDeviceShutdown"); mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN); + mProcessor->WriteMetricsActivationToDisk(getElapsedRealtimeNs()); return Status::ok(); } @@ -901,6 +903,7 @@ Status StatsService::statsCompanionReady() { void StatsService::Startup() { mConfigManager->Startup(); + mProcessor->LoadMetricsActivationFromDisk(); } void StatsService::Terminate() { diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto new file mode 100644 index 000000000000..0e9ee03dd2cb --- /dev/null +++ b/cmds/statsd/src/active_config_list.proto @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.os.statsd; +option java_package = "com.android.os"; +option java_multiple_files = true; +option java_outer_classname = "ActiveConfigProto"; + +message ActiveMetric { + // metric id + optional int64 metric_id = 1; + // Remaining time to live in nano seconds. -1 for infinity. + optional int64 time_to_live_nanos = 2; +} + +message ActiveConfig { + // config id + optional int64 config_id = 1; + // config uid + optional int32 uid = 2; + // metrics + repeated ActiveMetric active_metric = 3; +} + +// all configs and their metrics on device. +message ActiveConfigList { + repeated ActiveConfig active_config = 1; +}
\ No newline at end of file diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp index ad5eae361f1c..6b46b8b3b900 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -23,7 +23,6 @@ #include "external/Perfetto.h" #include "external/Perfprofd.h" -#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" #include "subscriber/IncidentdReporter.h" #include "subscriber/SubscriberReporter.h" diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index e33bd8c97a07..2a3eee2a7e18 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -82,4 +82,6 @@ extend google.protobuf.FieldOptions { optional bool is_uid = 50001 [default = false]; optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC]; + + optional bool allow_from_any_uid = 50003 [default = false]; }
\ No newline at end of file diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index fc3aa91b9e0b..7ddb783d71e1 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -41,12 +41,13 @@ import "frameworks/base/core/proto/android/service/procstats_enum.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/stats/enums.proto"; import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; +import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; +import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; -import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; -import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; +import "frameworks/base/core/proto/android/wifi/enums.proto"; /** * The master atom class. This message defines all of the available @@ -110,7 +111,7 @@ message Atom { PacketWakeupOccurred packet_wakeup_occurred = 44; WallClockTimeShifted wall_clock_time_shifted = 45; AnomalyDetected anomaly_detected = 46; - AppBreadcrumbReported app_breadcrumb_reported = 47; + AppBreadcrumbReported app_breadcrumb_reported = 47 [(allow_from_any_uid) = true]; AppStartOccurred app_start_occurred = 48; AppStartCanceled app_start_canceled = 49; AppStartFullyDrawn app_start_fully_drawn = 50; @@ -121,7 +122,7 @@ message Atom { AppStartMemoryStateCaptured app_start_memory_state_captured = 55; ShutdownSequenceReported shutdown_sequence_reported = 56; BootSequenceReported boot_sequence_reported = 57; - DaveyOccurred davey_occurred = 58; + DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true]; OverlayStateChanged overlay_state_changed = 59; ForegroundServiceStateChanged foreground_service_state_changed = 60; CallStateChanged call_state_changed = 61; @@ -214,7 +215,7 @@ message Atom { SpeechDspStatReported speech_dsp_stat_reported = 145; UsbContaminantReported usb_contaminant_reported = 146; WatchdogRollbackOccurred watchdog_rollback_occurred = 147; - BiometricHalDeathReported biometric_hal_death_reported = 148; + BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148; BubbleUIChanged bubble_ui_changed = 149; ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150; BluetoothActiveDeviceChanged bluetooth_active_device_changed = 151; @@ -239,6 +240,7 @@ message Atom { PermissionGrantRequestResultReported permission_grant_request_result_reported = 170; BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171; DeviceIdentifierAccessDenied device_identifier_access_denied = 172; + BubbleDeveloperErrorReported bubble_developer_error_reported = 173; } // Pulled events will start at field 10000. @@ -993,6 +995,9 @@ message WifiLockStateChanged { ON = 1; } optional State state = 2; + + // WifiLock type, from frameworks/base/core/proto/android/wifi/enums.proto. + optional android.net.wifi.WifiModeEnum mode = 3; } /** @@ -2081,9 +2086,9 @@ message BluetoothSocketConnectionStateChanged { // Salt: Randomly generated 256 bit value // Hash algorithm: HMAC-SHA256 // Size: 32 byte - // Default: null or empty if the device identifier is not known + // Default: null or empty if this is a server listener socket optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Port of this socket + // Temporary port of this socket for the current connection or session only // Default 0 when unknown or don't care optional int32 port = 2; // Socket type as mentioned in @@ -2097,6 +2102,14 @@ message BluetoothSocketConnectionStateChanged { optional int64 tx_bytes = 5; // Number of bytes received from remote device during this connection optional int64 rx_bytes = 6; + // Socket owner's UID + optional int32 uid = 7 [(is_uid) = true]; + // Server port of this socket, if any. When both |server_port| and |port| fields are populated, + // |port| must be spawned by |server_port| + // Default 0 when unknown or don't care + optional int32 server_port = 8; + // Whether this is a server listener socket + optional android.bluetooth.SocketRoleEnum is_server = 9; } /** @@ -3045,13 +3058,15 @@ message BiometricErrorOccurred { } /** - * Logs when a biometric HAL has crashed. + * Logs when a system health issue is detected. * Logged from: * frameworks/base/services/core/java/com/android/server/biometrics */ -message BiometricHalDeathReported { +message BiometricSystemHealthIssueDetected { // Biometric modality. optional android.hardware.biometrics.ModalityEnum modality = 1; + // Type of issue detected. + optional android.hardware.biometrics.IssueEnum issue = 2; } message Notification { @@ -5344,6 +5359,27 @@ message BubbleUIChanged { } /** + * Logs System UI bubbles developer errors. + * + * Logged from: + * frameworks/base/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java + */ +message BubbleDeveloperErrorReported { + + // The app package that is posting the bubble. + optional string package_name = 1; + + // Bubble developer error type enums. + enum Error { + UNKNOWN = 0; + ACTIVITY_INFO_MISSING = 1; + ACTIVITY_INFO_NOT_RESIZABLE = 2; + DOCUMENT_LAUNCH_NOT_ALWAYS = 3; + } + optional Error error = 2 [default = UNKNOWN]; +} + +/** * Logs that a constraint for a scheduled job has changed. * * Logged from: diff --git a/cmds/statsd/src/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h index 0d505cb49e8f..d2193f41b80a 100644 --- a/cmds/statsd/src/external/PullDataReceiver.h +++ b/cmds/statsd/src/external/PullDataReceiver.h @@ -28,9 +28,16 @@ namespace statsd { class PullDataReceiver : virtual public RefBase{ public: virtual ~PullDataReceiver() {} - virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) = 0; + /** + * @param data The pulled data. + * @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the + * bucket should be invalidated. + * @param originalPullTimeNs This is when all the pulls have been initiated (elapsed time). + */ + virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, + bool pullSuccess, int64_t originalPullTimeNs) = 0; }; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index c69384c7077f..ecdcd21d44dd 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -174,9 +174,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // Size of specific categories of files. Eg. Music. {android::util::CATEGORY_SIZE, {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, - // Number of fingerprints registered to each user. + // Number of fingerprints enrolled for each user. {android::util::NUM_FINGERPRINTS_ENROLLED, {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS_ENROLLED)}}, + // Number of faces enrolled for each user. + {android::util::NUM_FACES_ENROLLED, + {.puller = new StatsCompanionServicePuller(android::util::NUM_FACES_ENROLLED)}}, // ProcStats. {android::util::PROC_STATS, {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}}, @@ -358,12 +361,13 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { for (const auto& pullInfo : needToPull) { vector<shared_ptr<LogEvent>> data; - if (!Pull(pullInfo.first, &data)) { + bool pullSuccess = Pull(pullInfo.first, &data); + if (pullSuccess) { + StatsdStats::getInstance().notePullDelay( + pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs); + } else { VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs); - continue; } - StatsdStats::getInstance().notePullDelay(pullInfo.first, - getElapsedRealtimeNs() - elapsedTimeNs); // Convention is to mark pull atom timestamp at request time. // If we pull at t0, puller starts at t1, finishes at t2, and send back @@ -380,8 +384,8 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { for (const auto& receiverInfo : pullInfo.second) { sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote(); if (receiverPtr != nullptr) { - receiverPtr->onDataPulled(data); - // we may have just come out of a coma, compute next pull time + receiverPtr->onDataPulled(data, pullSuccess, elapsedTimeNs); + // We may have just come out of a coma, compute next pull time. int numBucketsAhead = (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs; receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs; diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 37ccad5f4a49..c4034ffeee22 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -423,31 +423,50 @@ void StatsdStats::noteEmptyData(int atomId) { mPulledAtomStats[atomId].emptyData++; } -void StatsdStats::noteHardDimensionLimitReached(int metricId) { +void StatsdStats::noteHardDimensionLimitReached(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).hardDimensionLimitReached++; } -void StatsdStats::noteLateLogEventSkipped(int metricId) { +void StatsdStats::noteLateLogEventSkipped(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).lateLogEventSkipped++; } -void StatsdStats::noteSkippedForwardBuckets(int metricId) { +void StatsdStats::noteSkippedForwardBuckets(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).skippedForwardBuckets++; } -void StatsdStats::noteBadValueType(int metricId) { +void StatsdStats::noteBadValueType(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).badValueType++; } -void StatsdStats::noteConditionChangeInNextBucket(int metricId) { +void StatsdStats::noteBucketDropped(int64_t metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).bucketDropped++; +} + +void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).conditionChangeInNextBucket++; } +void StatsdStats::noteInvalidatedBucket(int64_t metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).invalidatedBucket++; +} + +void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) { + lock_guard<std::mutex> lock(mLock); + AtomMetricStats& pullStats = getAtomMetricStats(metricId); + pullStats.maxBucketBoundaryDelayNs = + std::max(pullStats.maxBucketBoundaryDelayNs, timeDelayNs); + pullStats.minBucketBoundaryDelayNs = + std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); +} + StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) { auto atomMetricStatsIter = mAtomMetricStats.find(metricId); if (atomMetricStatsIter != mAtomMetricStats.end()) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 01e9ca17e5fd..2999b649a509 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -342,27 +342,43 @@ public: /** * Hard limit was reached in the cardinality of an atom */ - void noteHardDimensionLimitReached(int atomId); + void noteHardDimensionLimitReached(int64_t metricId); /** * A log event was too late, arrived in the wrong bucket and was skipped */ - void noteLateLogEventSkipped(int atomId); + void noteLateLogEventSkipped(int64_t metricId); /** * Buckets were skipped as time elapsed without any data for them */ - void noteSkippedForwardBuckets(int atomId); + void noteSkippedForwardBuckets(int64_t metricId); /** * An unsupported value type was received */ - void noteBadValueType(int atomId); + void noteBadValueType(int64_t metricId); + + /** + * Buckets were dropped due to reclaim memory. + */ + void noteBucketDropped(int64_t metricId); /** * A condition change was too late, arrived in the wrong bucket and was skipped */ - void noteConditionChangeInNextBucket(int atomId); + void noteConditionChangeInNextBucket(int64_t metricId); + + /** + * A bucket has been tagged as invalid. + */ + void noteInvalidatedBucket(int64_t metricId); + + /** + * For pulls at bucket boundaries, it represents the misalignment between the real timestamp and + * the end of the bucket. + */ + void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs); /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about @@ -408,6 +424,10 @@ public: long skippedForwardBuckets = 0; long badValueType = 0; long conditionChangeInNextBucket = 0; + long invalidatedBucket = 0; + long bucketDropped = 0; + int64_t minBucketBoundaryDelayNs = 0; + int64_t maxBucketBoundaryDelayNs = 0; } AtomMetricStats; private: diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 7cc57c12063c..350745b2c326 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -243,6 +243,7 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) { flushIfNeededLocked(dropTimeNs); + StatsdStats::getInstance().noteBucketDropped(mMetricId); mPastBuckets.clear(); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 69bafc354643..6c1c47bbc093 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -444,6 +444,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) { flushIfNeededLocked(dropTimeNs); + StatsdStats::getInstance().noteBucketDropped(mMetricId); mPastBuckets.clear(); } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index c53c4ce5e0d6..7e695a69988f 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -77,6 +77,7 @@ EventMetricProducer::~EventMetricProducer() { void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) { mProto->clear(); + StatsdStats::getInstance().noteBucketDropped(mMetricId); } void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index c2878f02a691..d56a355f15d6 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -406,9 +406,10 @@ std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const Lo return gaugeFields; } -void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { +void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, + bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (allData.size() == 0) { + if (!pullSuccess || allData.size() == 0) { return; } for (const auto& data : allData) { @@ -509,6 +510,7 @@ void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { void GaugeMetricProducer::dropDataLocked(const int64_t dropTimeNs) { flushIfNeededLocked(dropTimeNs); + StatsdStats::getInstance().noteBucketDropped(mMetricId); mPastBuckets.clear(); } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index df0877954d70..64a18337481b 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -67,7 +67,8 @@ public: virtual ~GaugeMetricProducer(); // Handles when the pulled data arrives. - void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override; + void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, + bool pullSuccess, int64_t originalPullTimeNs) override; // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index f87849edae7a..495138ee9b77 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -107,11 +107,57 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT if (it == mEventActivationMap.end()) { return; } + if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) { + it->second.state = ActivationState::kActiveOnBoot; + return; + } it->second.activation_ns = elapsedTimestampNs; it->second.state = ActivationState::kActive; mIsActive = true; } +void MetricProducer::setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs) { + if (mEventActivationMap.size() == 0) { + return; + } + for (auto& pair : mEventActivationMap) { + auto& activation = pair.second; + if (activation.ttl_ns >= remainingTtlNs) { + activation.activation_ns = currentTimeNs + remainingTtlNs - activation.ttl_ns; + activation.state = kActive; + mIsActive = true; + VLOG("setting new activation time to %lld, %lld, %lld", + (long long)activation.activation_ns, (long long)currentTimeNs, + (long long)remainingTtlNs); + return; + } + } + ALOGE("Required ttl is longer than all possible activations."); +} + +int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const { + int64_t maxTtl = 0; + for (const auto& activation : mEventActivationMap) { + if (activation.second.state == kActive) { + maxTtl = std::max(maxTtl, activation.second.ttl_ns + activation.second.activation_ns - + currentTimeNs); + } + } + return maxTtl; +} + +void MetricProducer::prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs) { + if (mActivationType != MetricActivation::ACTIVATE_ON_BOOT) { + return; + } + for (auto& activation : mEventActivationMap) { + if (activation.second.state == kActiveOnBoot) { + activation.second.state = kActive; + activation.second.activation_ns = currentTimeNs; + mIsActive = true; + } + } +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 09e240929f08..849cb76ec392 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -37,12 +37,13 @@ namespace statsd { // If the metric has no activation requirement, it will be active once the metric producer is // created. // If the metric needs to be activated by atoms, the metric producer will start -// with kNotActive state, turn to kActive when the activation event arrives, become kNotActive -// when it reaches the duration limit (timebomb). If the activation event arrives again before -// or after it expires, the event producer will be re-activated and ttl will be reset. +// with kNotActive state, turn to kActive or kActiveOnBoot when the activation event arrives, become +// kNotActive when it reaches the duration limit (timebomb). If the activation event arrives again +// before or after it expires, the event producer will be re-activated and ttl will be reset. enum ActivationState { kNotActive = 0, kActive = 1, + kActiveOnBoot = 2, }; // A MetricProducer is responsible for compute one single metrics, creating stats log report, and @@ -179,10 +180,21 @@ public: mBucketSizeNs = bucketSize; } - inline const int64_t& getMetricId() { + inline const int64_t& getMetricId() const { return mMetricId; } + int64_t getRemainingTtlNs(int64_t currentTimeNs) const { + std::lock_guard<std::mutex> lock(mMutex); + return getRemainingTtlNsLocked(currentTimeNs); + } + + // Set metric to active for ttlNs. + void setActive(int64_t currentTimeNs, int64_t remainingTtlNs) { + std::lock_guard<std::mutex> lock(mMutex); + setActiveLocked(currentTimeNs, remainingTtlNs); + } + // Let MetricProducer drop in-memory data to save memory. // We still need to keep future data valid and anomaly tracking work, which means we will // have to flush old data, informing anomaly trackers then safely drop old data. @@ -202,8 +214,22 @@ public: activateLocked(activationTrackerIndex, elapsedTimestampNs); } + bool isActive() const { + std::lock_guard<std::mutex> lock(mMutex); + return isActiveLocked(); + } + + void prepActiveForBootIfNecessary(int64_t currentTimeNs) { + std::lock_guard<std::mutex> lock(mMutex); + prepActiveForBootIfNecessaryLocked(currentTimeNs); + } + void addActivation(int activationTrackerIndex, int64_t ttl_seconds); + inline void setActivationType(const MetricActivation::ActivationType& activationType) { + mActivationType = activationType; + } + void flushIfExpire(int64_t elapsedTimestampNs); protected: @@ -227,6 +253,12 @@ protected: return mIsActive; } + void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs); + + int64_t getRemainingTtlNsLocked(int64_t currentTimeNs) const; + + void setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs); + /** * Flushes the current bucket if the eventTime is after the current bucket's end time. This will also flush the current partial bucket in memory. @@ -347,7 +379,12 @@ protected: bool mIsActive; + MetricActivation::ActivationType mActivationType; + FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); + + FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index dd969c0e9c20..6ed6ab500597 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -122,6 +122,13 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, StatsdStats::getInstance().noteConfigReceived( key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), mAllAnomalyTrackers.size(), mAnnotations, mConfigValid); + // Check active + for (const auto& metric : mAllMetricProducers) { + if (metric->isActive()) { + mIsActive = true; + break; + } + } } MetricsManager::~MetricsManager() { @@ -234,12 +241,22 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, VLOG("=========================Metric Reports End=========================="); } -// Consume the stats log if it's interesting to this metric. -void MetricsManager::onLogEvent(const LogEvent& event) { - if (!mConfigValid) { - return; + +bool MetricsManager::checkLogCredentials(const LogEvent& event) { + if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) != + android::util::AtomsInfo::kWhitelistedAtoms.end()) + { + return true; + } + std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); + if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { + VLOG("log source %d not on the whitelist", event.GetUid()); + return false; } + return true; +} +bool MetricsManager::eventSanityCheck(const LogEvent& event) { if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { // Check that app breadcrumb reported fields are valid. status_t err = NO_ERROR; @@ -249,23 +266,23 @@ void MetricsManager::onLogEvent(const LogEvent& event) { long appHookUid = event.GetLong(event.size()-2, &err); if (err != NO_ERROR ) { VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid"); - return; + return false; } int32_t loggerUid = event.GetUid(); if (loggerUid != appHookUid && loggerUid != AID_STATSD) { VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d", appHookUid, loggerUid); - return; + return false; } // The state must be from 0,3. This part of code must be manually updated. long appHookState = event.GetLong(event.size(), &err); if (err != NO_ERROR ) { VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field"); - return; + return false; } else if (appHookState < 0 || appHookState > 3) { VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); - return; + return false; } } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. @@ -276,36 +293,49 @@ void MetricsManager::onLogEvent(const LogEvent& event) { long jankUid = event.GetLong(1, &err); if (err != NO_ERROR ) { VLOG("Davey occurred had error when parsing the uid"); - return; + return false; } int32_t loggerUid = event.GetUid(); if (loggerUid != jankUid && loggerUid != AID_STATSD) { VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid, loggerUid); - return; + return false; } long duration = event.GetLong(event.size(), &err); if (err != NO_ERROR ) { VLOG("Davey occurred had error when parsing the duration"); - return; + return false; } else if (duration > 100000) { VLOG("Davey duration is unreasonably long: %ld", duration); - return; - } - } else { - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { - VLOG("log source %d not on the whitelist", event.GetUid()); - return; + return false; } } + return true; +} + +// Consume the stats log if it's interesting to this metric. +void MetricsManager::onLogEvent(const LogEvent& event) { + if (!mConfigValid) { + return; + } + + if (!checkLogCredentials(event)) { + return; + } + + if (!eventSanityCheck(event)) { + return; + } + int tagId = event.GetTagId(); int64_t eventTimeNs = event.GetElapsedTimestampNs(); + bool isActive = false; for (int metric : mMetricIndexesWithActivation) { mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); + isActive |= mAllMetricProducers[metric]->isActive(); } if (mTagIds.find(tagId) == mTagIds.end()) { @@ -323,10 +353,13 @@ void MetricsManager::onLogEvent(const LogEvent& event) { if (matcherCache[it.first] == MatchingState::kMatched) { for (int metricIndex : it.second) { mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); + isActive |= mAllMetricProducers[metricIndex]->isActive(); } } } + mIsActive = isActive; + // A bitmap to see which ConditionTracker needs to be re-evaluated. vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); @@ -418,6 +451,25 @@ size_t MetricsManager::byteSize() { return totalSize; } +void MetricsManager::setActiveMetrics(ActiveConfig config, int64_t currentTimeNs) { + if (config.active_metric_size() == 0) { + ALOGW("No active metric for config %s", mConfigKey.ToString().c_str()); + return; + } + + for (int i = 0; i < config.active_metric_size(); i++) { + for (int metric : mMetricIndexesWithActivation) { + if (mAllMetricProducers[metric]->getMetricId() == config.active_metric(i).metric_id()) { + VLOG("Setting active metric: %lld", + (long long)mAllMetricProducers[metric]->getMetricId()); + mAllMetricProducers[metric]->setActive( + currentTimeNs, config.active_metric(i).time_to_live_nanos()); + mIsActive = true; + } + } + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index a31efbd3c8a6..cb1cefbf2063 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -16,12 +16,13 @@ #pragma once -#include "external/StatsPullerManager.h" +#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> #include "anomaly/AlarmMonitor.h" #include "anomaly/AlarmTracker.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionTracker.h" #include "config/ConfigKey.h" +#include "external/StatsPullerManager.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "logd/LogEvent.h" #include "matchers/LogMatchingTracker.h" @@ -48,6 +49,10 @@ public: // Return whether the configuration is valid. bool isConfigValid() const; + bool checkLogCredentials(const LogEvent& event); + + bool eventSanityCheck(const LogEvent& event); + void onLogEvent(const LogEvent& event); void onAnomalyAlarmFired( @@ -123,6 +128,26 @@ public: // Does not change the state. virtual size_t byteSize(); + inline bool isActive() const { + return mIsActive; + } + + inline void getActiveMetrics(std::vector<MetricProducer*>& metrics) const { + for (const auto& metric : mAllMetricProducers) { + if (metric->isActive()) { + metrics.push_back(metric.get()); + } + } + } + + inline void prepForShutDown(int64_t currentTimeNs) { + for (const auto& metric : mAllMetricProducers) { + metric->prepActiveForBootIfNecessary(currentTimeNs); + } + } + + void setActiveMetrics(ActiveConfig config, int64_t currentTimeNs); + private: // For test only. inline int64_t getTtlEndNs() const { return mTtlEndNs; } @@ -216,6 +241,9 @@ private: // The metrics that don't need to be uploaded or even reported. std::set<int64_t> mNoReportMetricIds; + // Any metric active means the config is active. + bool mIsActive; + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); @@ -247,6 +275,9 @@ private: FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); + + FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 6aa8e842b021..ac6c27adceaa 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -104,6 +104,7 @@ ValueMetricProducer::ValueMetricProducer( mSkipZeroDiffOutput(metric.skip_zero_diff_output()), mUseZeroDefaultBase(metric.use_zero_default_base()), mHasGlobalBase(false), + 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()) { @@ -174,6 +175,7 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) { flushIfNeededLocked(dropTimeNs); + StatsdStats::getInstance().noteBucketDropped(mMetricId); mPastBuckets.clear(); } @@ -308,6 +310,15 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, } } +void ValueMetricProducer::invalidateCurrentBucket() { + if (!mCurrentBucketIsInvalid) { + // Only report once per invalid bucket. + StatsdStats::getInstance().noteInvalidatedBucket(mMetricId); + } + mCurrentBucketIsInvalid = true; + resetBase(); +} + void ValueMetricProducer::resetBase() { for (auto& slice : mCurrentSlicedBucket) { for (auto& interval : slice.second) { @@ -323,6 +334,7 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId); + invalidateCurrentBucket(); return; } @@ -346,50 +358,27 @@ void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { vector<std::shared_ptr<LogEvent>> allData; if (!mPullerManager->Pull(mPullTagId, &allData)) { ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); - resetBase(); - return; - } - const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs; - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId, - (long long)mMaxPullDelayNs); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - resetBase(); + invalidateCurrentBucket(); return; } - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - - if (timestampNs < mCurrentBucketStartTimeNs) { - // The data will be skipped in onMatchedLogEventInternalLocked, but we don't want to report - // for every event, just the pull - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - } - for (const auto& data : allData) { - // make a copy before doing and changes - LogEvent localCopy = data->makeCopy(); - localCopy.setElapsedTimestampNs(timestampNs); - if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == - MatchingState::kMatched) { - onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); - } - } - mHasGlobalBase = true; + accumulateEvents(allData, timestampNs, timestampNs); } int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) { return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs; } -void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { +void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, + bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); if (mCondition) { - if (allData.size() == 0) { - VLOG("Data pulled is empty"); - StatsdStats::getInstance().noteEmptyData(mPullTagId); + if (!pullSuccess) { + // If the pull failed, we won't be able to compute a diff. + invalidateCurrentBucket(); return; } + // For scheduled pulled data, the effective event time is snap to the nearest // bucket end. In the case of waking up from a deep sleep state, we will // attribute to the previous bucket end. If the sleep was long but not very long, we @@ -397,26 +386,70 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven // we pull at a later time than real bucket end. // If the sleep was very long, we skip more than one bucket before sleep. In this case, // if the diff base will be cleared and this new data will serve as new diff base. - int64_t realEventTime = allData.at(0)->GetElapsedTimestampNs(); - int64_t bucketEndTime = calcPreviousBucketEndTime(realEventTime) - 1; - if (bucketEndTime < mCurrentBucketStartTimeNs) { - VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", (long long)bucketEndTime, - (long long)mCurrentBucketStartTimeNs); - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - return; + int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1; + StatsdStats::getInstance().noteBucketBoundaryDelayNs( + mMetricId, originalPullTimeNs - bucketEndTime); + accumulateEvents(allData, originalPullTimeNs, bucketEndTime); + + // We can probably flush the bucket. Since we used bucketEndTime when calling + // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed. + flushIfNeededLocked(originalPullTimeNs); + + } else { + VLOG("No need to commit data on condition false."); + } +} + +void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, + int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) { + bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs; + if (isEventLate) { + VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", + (long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs); + StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); + invalidateCurrentBucket(); + return; + } + + const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs; + StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); + if (pullDelayNs > mMaxPullDelayNs) { + ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId, + (long long)mMaxPullDelayNs); + StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); + // We are missing one pull from the bucket which means we will not have a complete view of + // what's going on. + invalidateCurrentBucket(); + return; + } + + if (allData.size() == 0) { + VLOG("Data pulled is empty"); + StatsdStats::getInstance().noteEmptyData(mPullTagId); + } + + mMatchedMetricDimensionKeys.clear(); + for (const auto& data : allData) { + LogEvent localCopy = data->makeCopy(); + if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == + MatchingState::kMatched) { + localCopy.setElapsedTimestampNs(eventElapsedTimeNs); + onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); } - for (const auto& data : allData) { - LogEvent localCopy = data->makeCopy(); - if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == - MatchingState::kMatched) { - localCopy.setElapsedTimestampNs(bucketEndTime); - onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); + } + // If the new pulled data does not contains some keys we track in our intervals, we need to + // reset the base. + for (auto& slice : mCurrentSlicedBucket) { + bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first) + != mMatchedMetricDimensionKeys.end(); + if (!presentInPulledData) { + for (auto& interval : slice.second) { + interval.hasBase = false; } } - mHasGlobalBase = true; - } else { - VLOG("No need to commit data on condition false."); } + mMatchedMetricDimensionKeys.clear(); + mHasGlobalBase = true; } void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { @@ -514,6 +547,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn (long long)mCurrentBucketStartTimeNs); return; } + mMatchedMetricDimensionKeys.insert(eventKey); flushIfNeededLocked(eventTimeNs); @@ -679,31 +713,13 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, (int)mCurrentSlicedBucket.size()); int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs; - if (bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) { + bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; + if (isBucketLargeEnough && !mCurrentBucketIsInvalid) { // The current bucket is large enough to keep. for (const auto& slice : mCurrentSlicedBucket) { - ValueBucket bucket; - bucket.mBucketStartNs = mCurrentBucketStartTimeNs; - bucket.mBucketEndNs = bucketEndTime; - for (const auto& interval : slice.second) { - if (interval.hasValue) { - // skip the output if the diff is zero - if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) { - continue; - } - bucket.valueIndex.push_back(interval.valueIndex); - if (mAggregationType != ValueMetric::AVG) { - bucket.values.push_back(interval.value); - } else { - double sum = interval.value.type == LONG ? (double)interval.value.long_value - : interval.value.double_value; - bucket.values.push_back(Value((double)sum / interval.sampleSize)); - } - } - } + ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second); // it will auto create new vector of ValuebucketInfo if the key is not found. if (bucket.valueIndex.size() > 0) { auto& bucketList = mPastBuckets[slice.first]; @@ -714,6 +730,58 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime); } + if (!mCurrentBucketIsInvalid) { + appendToFullBucket(eventTimeNs, fullBucketEndTimeNs); + } + initCurrentSlicedBucket(); + mCurrentBucketIsInvalid = false; +} + +ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, + const std::vector<Interval>& intervals) { + ValueBucket bucket; + bucket.mBucketStartNs = mCurrentBucketStartTimeNs; + bucket.mBucketEndNs = bucketEndTime; + for (const auto& interval : intervals) { + if (interval.hasValue) { + // skip the output if the diff is zero + if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) { + continue; + } + bucket.valueIndex.push_back(interval.valueIndex); + if (mAggregationType != ValueMetric::AVG) { + bucket.values.push_back(interval.value); + } else { + double sum = interval.value.type == LONG ? (double)interval.value.long_value + : interval.value.double_value; + bucket.values.push_back(Value((double)sum / interval.sampleSize)); + } + } + } + return bucket; +} + +void ValueMetricProducer::initCurrentSlicedBucket() { + for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) { + bool obsolete = true; + for (auto& interval : it->second) { + interval.hasValue = false; + interval.sampleSize = 0; + if (interval.seenNewData) { + obsolete = false; + } + interval.seenNewData = false; + } + + if (obsolete) { + it = mCurrentSlicedBucket.erase(it); + } else { + it++; + } + } +} + +void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) { if (eventTimeNs > fullBucketEndTimeNs) { // If full bucket, send to anomaly tracker. // Accumulate partial buckets with current value and then send to anomaly tracker. if (mCurrentFullBucket.size() > 0) { @@ -751,24 +819,6 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { mCurrentFullBucket[slice.first] += slice.second[0].value.long_value; } } - - for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) { - bool obsolete = true; - for (auto& interval : it->second) { - interval.hasValue = false; - interval.sampleSize = 0; - if (interval.seenNewData) { - obsolete = false; - } - interval.seenNewData = false; - } - - if (obsolete) { - it = mCurrentSlicedBucket.erase(it); - } else { - it++; - } - } } size_t ValueMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index a8dfc5ba0e5d..d1c2315b28be 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -51,7 +51,8 @@ public: virtual ~ValueMetricProducer(); // Process data pulled on bucket boundary. - void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override; + void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, + bool pullSuccess, int64_t originalPullTimeNs) override; // ValueMetric needs special logic if it's a pulled atom. void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, @@ -60,6 +61,7 @@ public: if (!mSplitBucketForAppUpgrade) { return; } + flushIfNeededLocked(eventTimeNs - 1); if (mIsPulled && mCondition) { pullAndMatchEventsLocked(eventTimeNs - 1); } @@ -102,6 +104,9 @@ private: // Calculate previous bucket end time based on current time. int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs); + // Mark the data as invalid. + void invalidateCurrentBucket(); + const int mWhatMatcherIndex; sp<EventMatcherWizard> mEventMatcherWizard; @@ -111,6 +116,9 @@ private: // Value fields for matching. std::vector<Matcher> mFieldMatchers; + // Value fields for matching. + std::set<MetricDimensionKey> mMatchedMetricDimensionKeys; + // tagId for pulled data. -1 if this is not pulled const int mPullTagId; @@ -155,6 +163,14 @@ private: void pullAndMatchEventsLocked(const int64_t timestampNs); + void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, + int64_t originalPullTimeNs, int64_t eventElapsedTimeNs); + + ValueBucket buildPartialBucket(int64_t bucketEndTime, + const std::vector<Interval>& intervals); + void initCurrentSlicedBucket(); + void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs); + // Reset diff base and mHasGlobalBase void resetBase(); @@ -186,6 +202,12 @@ private: // diff against. bool mHasGlobalBase; + // Invalid bucket. There was a problem in collecting data in the current bucket so we cannot + // trust any of the data in this bucket. + // + // For instance, one pull failed. + bool mCurrentBucketIsInvalid; + const int64_t mMaxPullDelayNs; const bool mSplitBucketForAppUpgrade; @@ -197,6 +219,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade); + FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated); FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse); FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); @@ -216,8 +239,19 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFail); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); + FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); + FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged); + FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary); + FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 180a1ae07523..463b5a097585 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -725,6 +725,8 @@ bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, ALOGE("Invalid metric tracker index."); return false; } + allMetricProducers[metricTrackerIndex]->setActivationType( + metric_activation.activation_type()); metricsWithActivation.push_back(metricTrackerIndex); for (int j = 0; j < metric_activation.event_activation_size(); ++j) { const EventActivation& activation = metric_activation.event_activation(j); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index cca09ac017a3..6a07a3f169e0 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -417,6 +417,10 @@ message StatsdStatsReport { optional int64 skipped_forward_buckets = 4; optional int64 bad_value_type = 5; optional int64 condition_change_in_next_bucket = 6; + optional int64 invalidated_bucket = 7; + optional int64 bucket_dropped = 8; + optional int64 min_bucket_boundary_delay_ns = 9; + optional int64 max_bucket_boundary_delay_ns = 10; } repeated AtomMetricStats atom_metric_stats = 17; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 9c9985ed271c..aa8cfc508861 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -78,6 +78,10 @@ const int FIELD_ID_LATE_LOG_EVENT_SKIPPED = 3; const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4; const int FIELD_ID_BAD_VALUE_TYPE = 5; const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6; +const int FIELD_ID_INVALIDATED_BUCKET = 7; +const int FIELD_ID_BUCKET_DROPPED = 8; +const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; +const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; namespace { @@ -494,6 +498,14 @@ void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricSt (long long)pair.second.badValueType); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET, (long long)pair.second.conditionChangeInNextBucket); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET, + (long long)pair.second.invalidatedBucket); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED, + (long long)pair.second.bucketDropped); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS, + (long long)pair.second.minBucketBoundaryDelayNs); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS, + (long long)pair.second.maxBucketBoundaryDelayNs); protoOutput->end(token); } diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 9d3a66902804..5c6d548ad13a 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -379,6 +379,13 @@ message EventActivation { message MetricActivation { optional int64 metric_id = 1; + enum ActivationType { + UNKNOWN = 0; + ACTIVATE_IMMEDIATELY = 1; + ACTIVATE_ON_BOOT = 2; + } + optional ActivationType activation_type = 3; + repeated EventActivation event_activation = 2; } diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index 6e4b2c86c837..42cac0cc4122 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -17,32 +17,67 @@ #include "Log.h" #include "IncidentdReporter.h" -#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" #include <android/os/IIncidentManager.h> #include <android/os/IncidentReportArgs.h> +#include <android/util/ProtoOutputStream.h> #include <binder/IBinder.h> #include <binder/IServiceManager.h> +#include <vector> + namespace android { namespace os { namespace statsd { +using android::util::ProtoOutputStream; +using std::vector; + +using util::FIELD_TYPE_MESSAGE; +using util::FIELD_TYPE_INT32; +using util::FIELD_TYPE_INT64; + +// field ids in IncidentHeaderProto +const int FIELD_ID_ALERT_ID = 1; +const int FIELD_ID_CONFIG_KEY = 3; +const int FIELD_ID_CONFIG_KEY_UID = 1; +const int FIELD_ID_CONFIG_KEY_ID = 2; + +namespace { +void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) { + ProtoOutputStream headerProto; + headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id); + uint64_t token = + headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); + headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid()); + headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId()); + headerProto.end(token); + + protoData->resize(headerProto.size()); + size_t pos = 0; + auto iter = headerProto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&((*protoData)[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } +} +} // namespace + bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, const ConfigKey& configKey) { if (config.section_size() == 0) { VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, - configKey.GetUid(), (long long) configKey.GetId()); + configKey.GetUid(), (long long)configKey.GetId()); return false; } IncidentReportArgs incidentReport; - android::os::IncidentHeaderProto header; - header.set_alert_id(rule_id); - header.mutable_config_key()->set_uid(configKey.GetUid()); - header.mutable_config_key()->set_id(configKey.GetId()); - incidentReport.addHeader(header); + vector<uint8_t> protoData; + getProtoData(rule_id, configKey, &protoData); + incidentReport.addHeader(protoData); for (int i = 0; i < config.section_size(); i++) { incidentReport.addSection(config.section(i)); diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc index cbf2a8d5383d..e0cbd5d25716 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -26,3 +26,4 @@ on post-fs-data # Create directory for statsd mkdir /data/misc/stats-data/ 0770 statsd system mkdir /data/misc/stats-service/ 0770 statsd system + mkdir /data/misc/stats-active-metric/ 0770 statsd system diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index d52be441f6b6..60df165f102c 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -286,6 +286,415 @@ TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { EXPECT_TRUE(noData); } +TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { + int uid = 1111; + + // Setup a simple config, no activation + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + // Add another config, with two metrics, one with activation + StatsdConfig config2; + config2.set_id(12342); + config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + *config2.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId3 = 1234561; + long metricId4 = 1234562; + + auto countMetric3 = config2.add_count_metric(); + countMetric3->set_id(metricId3); + countMetric3->set_what(wakelockAcquireMatcher.id()); + countMetric3->set_bucket(FIVE_MINUTES); + + auto countMetric4 = config2.add_count_metric(); + countMetric4->set_id(metricId4); + countMetric4->set_what(wakelockAcquireMatcher.id()); + countMetric4->set_bucket(FIVE_MINUTES); + + auto metric3Activation = config2.add_metric_activation(); + metric3Activation->set_metric_id(metricId3); + auto metric3ActivationTrigger = metric3Activation->add_event_activation(); + metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric3ActivationTrigger->set_ttl_seconds(100); + + ConfigKey cfgKey2(uid, 12342); + + // Add another config, with two metrics, both with activations + StatsdConfig config3; + config3.set_id(12342); + config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + *config3.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId5 = 1234565; + long metricId6 = 1234566; + auto countMetric5 = config3.add_count_metric(); + countMetric5->set_id(metricId5); + countMetric5->set_what(wakelockAcquireMatcher.id()); + countMetric5->set_bucket(FIVE_MINUTES); + + auto countMetric6 = config3.add_count_metric(); + countMetric6->set_id(metricId6); + countMetric6->set_what(wakelockAcquireMatcher.id()); + countMetric6->set_bucket(FIVE_MINUTES); + + auto metric5Activation = config3.add_metric_activation(); + metric5Activation->set_metric_id(metricId5); + auto metric5ActivationTrigger = metric5Activation->add_event_activation(); + metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric5ActivationTrigger->set_ttl_seconds(100); + + auto metric6Activation = config3.add_metric_activation(); + metric6Activation->set_metric_id(metricId6); + auto metric6ActivationTrigger = metric6Activation->add_event_activation(); + metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric6ActivationTrigger->set_ttl_seconds(200); + + ConfigKey cfgKey3(uid, 12343); + + processor->OnConfigUpdated(2, cfgKey2, config2); + processor->OnConfigUpdated(3, cfgKey3, config3); + + EXPECT_EQ(3, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_TRUE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + it = processor->mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); + + metricIt = metricsManager2->mAllMetricProducers.begin(); + for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId3) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); + auto& metricProducer3 = *metricIt; + EXPECT_FALSE(metricProducer3->isActive()); + + metricIt = metricsManager2->mAllMetricProducers.begin(); + for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId4) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); + auto& metricProducer4 = *metricIt; + EXPECT_TRUE(metricProducer4->isActive()); + + it = processor->mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager3 = it->second; + EXPECT_FALSE(metricsManager3->isActive()); + + metricIt = metricsManager3->mAllMetricProducers.begin(); + for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId5) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); + auto& metricProducer5 = *metricIt; + EXPECT_FALSE(metricProducer5->isActive()); + + metricIt = metricsManager3->mAllMetricProducers.begin(); + for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId6) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); + auto& metricProducer6 = *metricIt; + EXPECT_FALSE(metricProducer6->isActive()); + + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); + processor->OnLogEvent(event.get()); + + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + EXPECT_TRUE(metricProducer3->isActive()); + int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime); + EXPECT_EQ(100, ttl3); + EXPECT_TRUE(metricProducer5->isActive()); + int64_t ttl5 = metricProducer5->getRemainingTtlNs(shutDownTime); + EXPECT_EQ(100, ttl5); + EXPECT_TRUE(metricProducer6->isActive()); + int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime); + EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6); + + processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC); + + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + processor2->OnConfigUpdated(timeBase2, cfgKey2, config2); + processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); + + EXPECT_EQ(3, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_TRUE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + it = processor2->mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1002 = it->second; + EXPECT_TRUE(metricsManager1002->isActive()); + + metricIt = metricsManager1002->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId3) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); + auto& metricProducer1003 = *metricIt; + EXPECT_FALSE(metricProducer1003->isActive()); + + metricIt = metricsManager1002->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId4) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); + auto& metricProducer1004 = *metricIt; + EXPECT_TRUE(metricProducer1004->isActive()); + + it = processor2->mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1003 = it->second; + EXPECT_FALSE(metricsManager1003->isActive()); + EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size()); + + metricIt = metricsManager1003->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId5) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); + auto& metricProducer1005 = *metricIt; + EXPECT_FALSE(metricProducer1005->isActive()); + + metricIt = metricsManager1003->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId6) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); + auto& metricProducer1006 = *metricIt; + EXPECT_FALSE(metricProducer1006->isActive()); + + EXPECT_FALSE(metricProducer1003->isActive()); + const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns); + EXPECT_EQ(0, activation1003.activation_ns); + EXPECT_FALSE(metricProducer1005->isActive()); + const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1005.ttl_ns); + EXPECT_EQ(0, activation1005.activation_ns); + EXPECT_FALSE(metricProducer1006->isActive()); + const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second; + EXPECT_EQ(200 * NS_PER_SEC, activation1006.ttl_ns); + EXPECT_EQ(0, activation1006.activation_ns); + + processor2->LoadMetricsActivationFromDisk(); + + EXPECT_TRUE(metricProducer1003->isActive()); + EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns); + EXPECT_TRUE(metricProducer1005->isActive()); + EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns); + EXPECT_TRUE(metricProducer1006->isActive()); + EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns); +} + +TEST(StatsLogProcessorTest, TestActivationOnBoot) { + int uid = 1111; + + // Setup a simple config, no activation + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(MetricActivation::ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger = metric1Activation->add_event_activation(); + metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger->set_ttl_seconds(100); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_FALSE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1.ttl_ns); + EXPECT_EQ(0, activation1.activation_ns); + EXPECT_EQ(kNotActive, activation1.state); + + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); + processor->OnLogEvent(event.get()); + + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1.activation_ns); + EXPECT_EQ(kActiveOnBoot, activation1.state); + + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + + processor->WriteMetricsActivationToDisk(shutDownTime); + EXPECT_TRUE(metricProducer1->isActive()); + int64_t ttl1 = metricProducer1->getRemainingTtlNs(shutDownTime); + EXPECT_EQ(100 * NS_PER_SEC, ttl1); + + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_FALSE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1001.ttl_ns); + EXPECT_EQ(0, activation1001.activation_ns); + EXPECT_EQ(kNotActive, activation1001.state); + + processor2->LoadMetricsActivationFromDisk(); + + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001.ttl_ns, activation1001.activation_ns); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 0ffbb54c8d48..62868232d8e7 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -133,7 +133,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { event->init(); allData.push_back(event); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); EXPECT_EQ(INT, it->mValue.getType()); @@ -151,7 +151,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { event2->write(25); event2->init(); allData.push_back(event2); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); EXPECT_EQ(INT, it->mValue.getType()); @@ -305,7 +305,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { event->write(1); event->init(); allData.push_back(event); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -328,7 +328,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { event->write(3); event->init(); allData.push_back(event); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() @@ -371,7 +371,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { event->write(1); event->init(); allData.push_back(event); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -440,7 +440,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { event->write(110); event->init(); allData.push_back(event); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() @@ -541,7 +541,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { event->write(110); event->init(); allData.push_back(event); - gaugeProducer.onDataPulled(allData); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); @@ -590,7 +590,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { event1->write(13); event1->init(); - gaugeProducer.onDataPulled({event1}); + gaugeProducer.onDataPulled({event1}, /** succeed */ true, bucketStartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -604,7 +604,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { event2->write(15); event2->init(); - gaugeProducer.onDataPulled({event2}); + gaugeProducer.onDataPulled({event2}, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -619,7 +619,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { event3->write(26); event3->init(); - gaugeProducer.onDataPulled({event3}); + gaugeProducer.onDataPulled({event3}, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -633,7 +633,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10); event4->write("some value"); event4->init(); - gaugeProducer.onDataPulled({event4}); + gaugeProducer.onDataPulled({event4}, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); } diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index c0648ee70032..ae3cdbcb5eb4 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -160,16 +160,17 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -177,18 +178,19 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { event->write(23); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(23, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(12, curInterval.value.long_value); 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(2UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -196,17 +198,86 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(13, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second[2].values[0].long_value); +} + +TEST(ValueMetricProducerTest, TestPartialBucketCreated) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Initialize bucket. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); + event->write(tagId); + event->write(1); + event->init(); + data->push_back(event); + return true; + })) + // Partial bucket. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); + event->write(tagId); + event->write(5); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + // First bucket ends. + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); + event->write(tagId); + event->write(2); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData, /** success */ true, bucket2StartTimeNs); + + // Partial buckets created in 2nd bucket. + valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); + + // One full bucket and one partial bucket. + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + vector<ValueBucket> buckets = valueProducer.mPastBuckets.begin()->second; + EXPECT_EQ(2UL, buckets.size()); + // Full bucket (2 - 1) + EXPECT_EQ(1, buckets[0].values[0].long_value); + // Full bucket (5 - 3) + EXPECT_EQ(3, buckets[1].values[0].long_value); } /* @@ -256,7 +327,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = @@ -264,9 +335,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -274,17 +346,16 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { event->write(23); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); - // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + // No new data seen, so data has been cleared. + EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); - // no events caused flush of bucket - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -292,7 +363,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -341,7 +412,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -357,15 +428,16 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { event->write(10); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(10, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -373,16 +445,17 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(26, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); } /* @@ -420,7 +493,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -436,7 +509,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { event->write(10); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -451,14 +524,15 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(26, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); } /* @@ -525,16 +599,17 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(110, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); @@ -635,7 +710,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); @@ -650,9 +725,9 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { event->write(150); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); - EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); } @@ -689,12 +764,12 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); } TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { @@ -993,7 +1068,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -1011,16 +1086,16 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { event->write(23); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; // tartUpdated:false sum:12 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(23, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(12, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); // pull 3 come late. // The previous bucket gets closed with error. (Has start value 23, no ending) @@ -1032,7 +1107,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; // startUpdated:false sum:12 @@ -1120,7 +1195,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); @@ -1216,7 +1291,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { EXPECT_EQ(20, curInterval.value.long_value); EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); - // Now the alarm is delivered, but it is considered late, it has no effect + // Now the alarm is delivered, but it is considered late, the bucket is invalidated. vector<shared_ptr<LogEvent>> allData; allData.clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50); @@ -1224,10 +1299,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); + EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(130, curInterval.base.long_value); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(20, curInterval.value.long_value); @@ -1677,11 +1752,11 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { allData.push_back(event1); allData.push_back(event2); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(11, interval1.base.long_value); - EXPECT_EQ(true, interval1.hasValue); + EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); auto it = valueProducer.mCurrentSlicedBucket.begin(); @@ -1695,9 +1770,14 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval2.hasBase); EXPECT_EQ(4, interval2.base.long_value); - EXPECT_EQ(true, interval2.hasValue); + EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(4, interval2.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + + EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); + auto iterator = valueProducer.mPastBuckets.begin(); + EXPECT_EQ(8, iterator->second[0].values[0].long_value); + iterator++; + EXPECT_EQ(4, iterator->second[0].values[0].long_value); } /* @@ -1762,11 +1842,11 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { allData.push_back(event1); allData.push_back(event2); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(11, interval1.base.long_value); - EXPECT_EQ(true, interval1.hasValue); + EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); auto it = valueProducer.mCurrentSlicedBucket.begin(); @@ -1780,9 +1860,9 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval2.hasBase); EXPECT_EQ(4, interval2.base.long_value); - EXPECT_EQ(true, interval2.hasValue); + EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(4, interval2.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); @@ -1791,13 +1871,13 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { event1->write(5); event1->init(); allData.push_back(event1); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(5, interval2.base.long_value); + EXPECT_EQ(4, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(false, interval1.hasBase); + EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(true, valueProducer.mHasGlobalBase); EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); @@ -1813,17 +1893,17 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { event2->write(5); event2->init(); allData.push_back(event2); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(13, interval2.base.long_value); - EXPECT_EQ(true, interval2.hasValue); - EXPECT_EQ(8, interval2.value.long_value); + EXPECT_EQ(5, interval2.base.long_value); + EXPECT_EQ(false, interval2.hasValue); + EXPECT_EQ(5, interval2.value.long_value); EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(5, interval1.base.long_value); - EXPECT_EQ(true, interval1.hasValue); - EXPECT_EQ(5, interval1.value.long_value); + EXPECT_EQ(13, interval1.base.long_value); + EXPECT_EQ(false, interval1.hasValue); + EXPECT_EQ(8, interval1.value.long_value); EXPECT_EQ(true, valueProducer.mHasGlobalBase); EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); } @@ -1888,13 +1968,15 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { allData.push_back(event1); allData.push_back(event2); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(11, interval1.base.long_value); - EXPECT_EQ(true, interval1.hasValue); + EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); - EXPECT_TRUE(interval1.seenNewData); + EXPECT_FALSE(interval1.seenNewData); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); auto it = valueProducer.mCurrentSlicedBucket.begin(); for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) { @@ -1908,8 +1990,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(true, interval2.hasBase); EXPECT_EQ(4, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); - EXPECT_TRUE(interval2.seenNewData); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_FALSE(interval2.seenNewData); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); @@ -1918,40 +2000,92 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { event1->write(5); event1->init(); allData.push_back(event1); - valueProducer.onDataPulled(allData); - - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); - - EXPECT_EQ(false, interval1.hasBase); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - // on probation now - EXPECT_FALSE(interval1.seenNewData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + // Only one interval left. One was trimmed. + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval2.hasBase); EXPECT_EQ(5, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); - // back to good status - EXPECT_TRUE(interval2.seenNewData); + EXPECT_FALSE(interval2.seenNewData); EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); allData.clear(); event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1); event1->write(2); - event1->write(13); + event1->write(14); event1->init(); allData.push_back(event1); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(13, interval2.base.long_value); - EXPECT_EQ(true, interval2.hasValue); - EXPECT_EQ(8, interval2.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(14, interval2.base.long_value); + EXPECT_EQ(false, interval2.hasValue); + EXPECT_FALSE(interval2.seenNewData); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); + auto iterator = valueProducer.mPastBuckets.begin(); + EXPECT_EQ(9, iterator->second[0].values[0].long_value); + iterator++; + EXPECT_EQ(8, iterator->second[0].values[0].long_value); } -TEST(ValueMetricProducerTest, TestResetBaseOnPullFail) { +TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + // Used by onConditionChanged. + 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 + 8); + event->write(tagId); + event->write(100); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + 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); + + vector<shared_ptr<LogEvent>> allData; + valueProducer.onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(false, valueProducer.mHasGlobalBase); +} + +TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { ValueMetric metric; metric.set_id(metricId); metric.set_bucket(ONE_MINUTE); @@ -2007,7 +2141,57 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFail) { EXPECT_EQ(false, valueProducer.mHasGlobalBase); } -TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { +TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + 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 + 8); + event->write(tagId); + event->write(100); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.mCondition = true; + + vector<shared_ptr<LogEvent>> allData; + valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); + EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); + + valueProducer.onConditionChanged(false, bucketStartTimeNs + 1); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval& curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(false, valueProducer.mHasGlobalBase); +} + +TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { ValueMetric metric; metric.set_id(metricId); metric.set_bucket(ONE_MINUTE); @@ -2030,7 +2214,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { 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, bucket2StartTimeNs + 1); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); event->write(tagId); event->write(120); event->init(); @@ -2042,37 +2226,599 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = false; + + // Max delay is set to 0 so pull will exceed max delay. + valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); + EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); +} + +TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucket2StartTimeNs, + bucket2StartTimeNs, pullerManager); + + valueProducer.mCondition = false; + + // Event should be skipped since it is from previous bucket. + // Pull should not be called. + valueProducer.onConditionChanged(true, bucketStartTimeNs); + EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); +} + +TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + 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 + 1); + event->write(tagId); + event->write(100); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.mCondition = false; + valueProducer.mHasGlobalBase = false; + + valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); valueProducer.mHasGlobalBase = true; + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + 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); + EXPECT_EQ(true, valueProducer.mHasGlobalBase); +} + +TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .WillOnce(Return(false)) + // Second onConditionChanged + .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(130); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + valueProducer.mCondition = true; + + // Bucket start. vector<shared_ptr<LogEvent>> allData; allData.clear(); - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); event->write(1); event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData); + valueProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - // has one slice + // This will fail and should invalidate the whole bucket since we do not have all the data + // needed to compute the metric value when the screen was on. + valueProducer.onConditionChanged(false, bucketStartTimeNs + 2); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 3); + + // Bucket end. + allData.clear(); + shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event2->write(1); + event2->write(140); + event2->init(); + allData.push_back(event2); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1); + + EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + // Contains base from last pull which was successful. EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(110, curInterval.base.long_value); + EXPECT_EQ(140, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(true, valueProducer.mHasGlobalBase); +} + +TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .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(120); + event->init(); + data->push_back(event); + return true; + })) + // Second onConditionChanged + .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(130); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.mCondition = true; + + // Bucket start. + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); + event->write(1); + event->write(110); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); + + valueProducer.onConditionChanged(false, bucketStartTimeNs + 2); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 3); + + // Bucket end. + allData.clear(); + shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event2->write(1); + event2->write(140); + event2->init(); + allData.push_back(event2); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1); + EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + // Contains base from last pull which was successful. + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval& curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasBase); + EXPECT_EQ(140, curInterval.base.long_value); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer.mHasGlobalBase); +} - valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); +TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); - // has one slice + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .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(120); + event->init(); + data->push_back(event); + return true; + })) + // Second onConditionChanged + .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(130); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.mCondition = true; + + // Bucket start. + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); + event->write(1); + event->write(110); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + + // This will fail and should invalidate the whole bucket since we do not have all the data + // needed to compute the metric value when the screen was on. + valueProducer.onConditionChanged(false, bucketStartTimeNs + 2); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 3); + + // Bucket end. + allData.clear(); + shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event2->write(1); + event2->write(140); + event2->init(); + allData.push_back(event2); + valueProducer.onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + + valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1); + + EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + // Last pull failed so based has been reset. EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval& curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(false, valueProducer.mHasGlobalBase); +} + +TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Start bucket. + .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(3); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + // Bucket 2 start. + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(tagId); + event->write(110); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + + // Bucket 3 empty. + allData.clear(); + shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); + event2->init(); + allData.push_back(event2); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + // Data has been trimmed. + EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); +} + +TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .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(3); + event->init(); + data->push_back(event); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval& curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasBase); + EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(true, valueProducer.mHasGlobalBase); + + // Empty pull. + valueProducer.onConditionChanged(false, bucketStartTimeNs + 10); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer.mHasGlobalBase); } +TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .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(1); + 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, bucketStartTimeNs); + event->write(tagId); + event->write(2); + 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, bucketStartTimeNs); + event->write(tagId); + event->write(5); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); + valueProducer.onConditionChanged(false, bucketStartTimeNs + 11); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 12); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval& curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasBase); + EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(true, valueProducer.mHasGlobalBase); + + // End of bucket + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + // Data is empty, base should be reset. + EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(5, curInterval.base.long_value); + EXPECT_EQ(false, curInterval.hasValue); + EXPECT_EQ(true, valueProducer.mHasGlobalBase); + + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); +} + + +TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.mutable_dimensions_in_what()->set_field(tagId); + metric.mutable_dimensions_in_what()->add_child()->set_field(1); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .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(1); + event->write(1); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + + // End of bucket + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(2); + event->write(2); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // Key 1 should be reset since in not present in the most pull. + EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); + auto iterator = valueProducer.mCurrentSlicedBucket.begin(); + EXPECT_EQ(true, iterator->second[0].hasBase); + EXPECT_EQ(2, iterator->second[0].base.long_value); + EXPECT_EQ(false, iterator->second[0].hasValue); + iterator++; + EXPECT_EQ(false, iterator->second[0].hasBase); + EXPECT_EQ(1, iterator->second[0].base.long_value); + EXPECT_EQ(false, iterator->second[0].hasValue); + + EXPECT_EQ(true, valueProducer.mHasGlobalBase); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index 920a52dad641..d29e68e5a187 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -19,6 +19,7 @@ package com.android.commands.svc; import android.content.Context; import android.os.BatteryManager; import android.os.IPowerManager; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -71,7 +72,8 @@ public class PowerCommand extends Svc.Command { if (val != 0) { // if the request is not to set it to false, wake up the screen so that // it can stay on as requested - pm.wakeUp(SystemClock.uptimeMillis(), "PowerCommand", null); + pm.wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_UNKNOWN, "PowerCommand", null); } pm.setStayOnSetting(val); } diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index 6061b66a2ae8..7edd1287d378 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -33456,7 +33456,6 @@ HSPLandroid/view/SurfaceControl;->destroy()V HSPLandroid/view/SurfaceControl;->finalize()V HSPLandroid/view/SurfaceControl;->getActiveColorMode(Landroid/os/IBinder;)I HSPLandroid/view/SurfaceControl;->getActiveConfig(Landroid/os/IBinder;)I -HSPLandroid/view/SurfaceControl;->getBuiltInDisplay(I)Landroid/os/IBinder; HSPLandroid/view/SurfaceControl;->getDisplayColorModes(Landroid/os/IBinder;)[I HSPLandroid/view/SurfaceControl;->getDisplayConfigs(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo; HSPLandroid/view/SurfaceControl;->getHandle()Landroid/os/IBinder; diff --git a/config/hiddenapi-greylist-max-p.txt b/config/hiddenapi-greylist-max-p.txt index 7840b186615f..4c643e1c6831 100644 --- a/config/hiddenapi-greylist-max-p.txt +++ b/config/hiddenapi-greylist-max-p.txt @@ -48,6 +48,8 @@ Landroid/os/WorkSource;->updateLocked(Landroid/os/WorkSource;ZZ)Z Landroid/service/carrier/ICarrierMessagingCallback$Stub;-><init>()V Landroid/service/carrier/ICarrierMessagingService;->filterSms(Landroid/service/carrier/MessagePdu;Ljava/lang/String;IILandroid/service/carrier/ICarrierMessagingCallback;)V Landroid/telephony/CarrierMessagingServiceManager;-><init>()V +Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats; Landroid/view/IWindowManager;->setInTouchMode(Z)V Landroid/view/IWindowManager;->showStrictModeViolation(Z)V Lcom/android/internal/R$styleable;->AndroidManifestActivityAlias:[I diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 11bb38beee87..010e4478459d 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -328,17 +328,12 @@ Landroid/content/om/IOverlayManager;->getAllOverlays(I)Ljava/util/Map; Landroid/content/om/IOverlayManager;->getOverlayInfo(Ljava/lang/String;I)Landroid/content/om/OverlayInfo; Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver; -Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver; -Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V @@ -437,8 +432,6 @@ Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;-><init>(Landroid/os/IBinde Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver; -Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService; @@ -835,7 +828,6 @@ Landroid/os/IPowerManager;->isInteractive()Z Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V Landroid/os/IPowerManager;->userActivity(JII)V -Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem; Landroid/os/IRemoteCallback$Stub;-><init>()V Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V @@ -1482,8 +1474,6 @@ Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder; Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager; Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V Landroid/view/IDockedStackListener$Stub;-><init>()V -Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats; Landroid/view/IRecentsAnimationController;->finish(Z)V Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot; Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index a0464dfac1f4..ebb03e765a47 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -20,6 +20,7 @@ import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Looper; import android.os.Trace; import android.util.AndroidRuntimeException; @@ -76,7 +77,13 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio /** * Internal constants */ - @UnsupportedAppUsage + + /** + * System-wide animation scale. + * + * <p>To check whether animations are enabled system-wise use {@link #areAnimatorsEnabled()}. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static float sDurationScale = 1.0f; /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 21d66e567a81..cc419b8d837a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4565,16 +4565,25 @@ public final class ActivityThread extends ClientTransactionHandler { } private void onCoreSettingsChange() { - boolean debugViewAttributes = - mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0; - if (debugViewAttributes != View.mDebugViewAttributes) { - View.mDebugViewAttributes = debugViewAttributes; - + if (updateDebugViewAttributeState()) { // request all activities to relaunch for the changes to take place relaunchAllActivities(); } } + private boolean updateDebugViewAttributeState() { + boolean previousState = View.sDebugViewAttributes; + + View.sDebugViewAttributesApplicationPackage = mCoreSettings.getString( + Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, ""); + String currentPackage = (mBoundApplication != null && mBoundApplication.appInfo != null) + ? mBoundApplication.appInfo.packageName : ""; + View.sDebugViewAttributes = + mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0 + || View.sDebugViewAttributesApplicationPackage.equals(currentPackage); + return previousState != View.sDebugViewAttributes; + } + private void relaunchAllActivities() { for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { final Activity activity = entry.getValue().activity; @@ -5950,8 +5959,7 @@ public final class ActivityThread extends ClientTransactionHandler { // true : use 24 hour format. DateFormat.set24HourTimePref(is24Hr); - View.mDebugViewAttributes = - mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0; + updateDebugViewAttributeState(); StrictMode.initThreadDefaults(data.appInfo); StrictMode.initVmDefaults(data.appInfo); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 5814e69c36a3..ce7199816726 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -26,6 +26,7 @@ import android.app.ActivityManager.StackInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.Insets; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.RemoteException; @@ -84,6 +85,8 @@ public class ActivityView extends ViewGroup { /** The ActivityView is only allowed to contain one task. */ private final boolean mSingleTaskInstance; + private Insets mForwardedInsets; + @UnsupportedAppUsage public ActivityView(Context context) { this(context, null /* attrs */); @@ -369,11 +372,13 @@ public class ActivityView extends ViewGroup { .build(); try { + // TODO: Find a way to consolidate these calls to the server. wm.reparentDisplayContent(displayId, mRootSurfaceControl); wm.dontOverrideDisplayInfo(displayId); if (mSingleTaskInstance) { mActivityTaskManager.setDisplayToSingleTaskInstance(displayId); } + wm.setForwardedInsets(displayId, mForwardedInsets); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -454,6 +459,24 @@ public class ActivityView extends ViewGroup { } /** + * Set forwarded insets on the virtual display. + * + * @see IWindowManager#setForwardedInsets + */ + public void setForwardedInsets(Insets insets) { + mForwardedInsets = insets; + if (mVirtualDisplay == null) { + return; + } + try { + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + wm.setForwardedInsets(mVirtualDisplay.getDisplay().getDisplayId(), mForwardedInsets); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** * A task change listener that detects background color change of the topmost stack on our * virtual display and updates the background of the surface view. This background will be shown * when surface view is resized, but the app hasn't drawn its content in new size yet. diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index ea145f0b9d21..64b94a946489 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -189,9 +189,19 @@ public class AppOpsManager { /** * Special mode that means "allow only when app is in foreground." This is <b>not</b> - * returned from {@link #checkOp}, {@link #noteOp}, {@link #startOp}; rather, when this - * mode is set, these functions will return {@link #MODE_ALLOWED} when the app being - * checked is currently in the foreground, otherwise {@link #MODE_IGNORED}. + * returned from {@link #unsafeCheckOp}, {@link #noteOp}, {@link #startOp}. Rather, + * {@link #unsafeCheckOp} will always return {@link #MODE_ALLOWED} (because it is always + * possible for it to be ultimately allowed, depending on the app's background state), + * and {@link #noteOp} and {@link #startOp} will return {@link #MODE_ALLOWED} when the app + * being checked is currently in the foreground, otherwise {@link #MODE_IGNORED}. + * + * <p>The only place you will this normally see this value is through + * {@link #unsafeCheckOpRaw}, which returns the actual raw mode of the op. Note that because + * you can't know the current state of the app being checked (and it can change at any + * point), you can only treat the result here as an indication that it will vary between + * {@link #MODE_ALLOWED} and {@link #MODE_IGNORED} depending on changes in the background + * state of the app. You thus must always use {@link #noteOp} or {@link #startOp} to do + * the actual check for access to the op.</p> */ public static final int MODE_FOREGROUND = 4; diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index f522d71afd08..17f645dfbf23 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -125,7 +125,7 @@ public class KeyguardManager { public static final int RESULT_ALTERNATE = 1; /** - * @deprecated see {@link BiometricPrompt.Builder#setEnableFallback(boolean)} + * @deprecated see {@link BiometricPrompt.Builder#setAllowDeviceCredential(boolean)} * * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics * if enrolled) for the current user of the device. The caller is expected to launch this diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 028e3efc9fb8..dc4f3432b6bd 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8243,6 +8243,7 @@ public class Notification implements Parcelable private void buildIntoRemoteViewContent(RemoteViews remoteViews, RemoteViews customContent, TemplateBindResult result) { + int childIndex = -1; if (customContent != null) { // Need to clone customContent before adding, because otherwise it can no longer be // parceled independently of remoteViews. @@ -8250,7 +8251,11 @@ public class Notification implements Parcelable remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */); remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); + childIndex = 0; } + remoteViews.setIntTag(R.id.notification_main_column, + com.android.internal.R.id.notification_custom_view_index_tag, + childIndex); // also update the end margin if there is an image Resources resources = mBuilder.mContext.getResources(); int endMargin = resources.getDimensionPixelSize( diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index e95d62fc96eb..cfe27c752581 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -85,7 +85,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; private static final String ATT_GROUP = "group"; private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; - private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; + private static final String ATT_ALLOW_BUBBLE = "can_bubble"; private static final String DELIMITER = ","; /** @@ -611,14 +611,6 @@ public final class NotificationChannel implements Parcelable { * shade, in a floating window on top of other apps. */ public boolean canBubble() { - return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH; - } - - /** - * Like {@link #canBubble()}, but only checks the permission, not the importance. - * @hide - */ - public boolean isBubbleAllowed() { return mAllowBubbles; } diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 78ec8a17e8e8..65859c76a42a 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -6,5 +6,13 @@ { "path": "frameworks/base/services/core/java/com/android/server/wm" } + ], + "presubmit": [ + { + "name": "CtsFragmentTestCases" + }, + { + "name": "CtsFragmentTestCasesSdk26" + } ] } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 428c9b01d101..886115103ccf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4618,6 +4618,10 @@ public class DevicePolicyManager { * <p>If the installer must have access to the credentials, call * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, boolean)} instead. * + * <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps + * have been given to access the key and certificates associated with this alias will be + * revoked. + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or * {@code null} if calling from a delegated certificate installer. * @param privKey The private key to install. @@ -4645,6 +4649,10 @@ public class DevicePolicyManager { * immediately, without user approval. It is a best practice not to request this unless strictly * necessary since it opens up additional security vulnerabilities. * + * <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps + * have been given to access the key and certificates associated with this alias will be + * revoked. + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or * {@code null} if calling from a delegated certificate installer. * @param privKey The private key to install. @@ -4685,6 +4693,10 @@ public class DevicePolicyManager { * <p>Include {@link #INSTALLKEY_SET_USER_SELECTABLE} in the {@code flags} argument to allow * the user to select the key from a dialog. * + * <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps + * have been given to access the key and certificates associated with this alias will be + * revoked. + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or * {@code null} if calling from a delegated certificate installer. * @param privKey The private key to install. @@ -4761,6 +4773,10 @@ public class DevicePolicyManager { * <p>Because this method might take several seconds to complete, it should only be called from * a worker thread. This method returns {@code null} when called from the main thread. * + * <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps + * have been given to access the key and certificates associated with this alias will be + * revoked. + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or * {@code null} if calling from a delegated certificate installer. * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}. @@ -6408,11 +6424,9 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageName The name of the package to set as the default SMS application. * @throws SecurityException if {@code admin} is not a device owner. - * - * @hide */ - @UnsupportedAppUsage - public void setDefaultSmsApplication(@NonNull ComponentName admin, String packageName) { + public void setDefaultSmsApplication(@NonNull ComponentName admin, + @NonNull String packageName) { throwIfParentInstance("setDefaultSmsApplication"); if (mService != null) { try { @@ -9630,7 +9644,7 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param enabled {@code true} to enable the backup service, {@code false} to disable it. - * @throws SecurityException if {@code admin} is not a device owner. + * @throws SecurityException if {@code admin} is not a device owner or a profile owner. */ public void setBackupServiceEnabled(@NonNull ComponentName admin, boolean enabled) { throwIfParentInstance("setBackupServiceEnabled"); diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index a6f6d06ae71a..868fbfeeee39 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -751,6 +751,47 @@ public class BackupManager { } /** + * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the + * serial number of the its ancestral work profile or {@code null} if there is none. + * + * <p> The ancestral serial number will have a corresponding {@link UserHandle} if the device + * has a work profile that was restored from another work profile with serial number + * {@code ancestralSerialNumber}. + * + * @see UserManager#getSerialNumberForUser(UserHandle) + */ + @Nullable + public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) { + if (sService != null) { + try { + return sService.getUserForAncestralSerialNumber(ancestralSerialNumber); + } catch (RemoteException e) { + Log.e(TAG, "getUserForAncestralSerialNumber() couldn't connect"); + } + } + return null; + } + + /** + * Sets the ancestral work profile for the calling user. + * + * <p> The ancestral work profile corresponds to the profile that was used to restore to the + * callers profile. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) + public void setAncestralSerialNumber(long ancestralSerialNumber) { + if (sService != null) { + try { + sService.setAncestralSerialNumber(ancestralSerialNumber); + } catch (RemoteException e) { + Log.e(TAG, "setAncestralSerialNumber() couldn't connect"); + } + } + } + + /** * Returns an {@link Intent} for the specified transport's configuration UI. * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String, * Intent, String)}. diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index eda8981d7e0e..8386c72e3406 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -22,6 +22,7 @@ import android.app.backup.IFullBackupRestoreObserver; import android.app.backup.IRestoreSession; import android.app.backup.ISelectBackupTransportCallback; import android.os.ParcelFileDescriptor; +import android.os.UserHandle; import android.content.Intent; import android.content.ComponentName; @@ -685,4 +686,24 @@ interface IBackupManager { * {@link android.app.backup.IBackupManager.cancelBackups} for the calling user id. */ void cancelBackups(); + + /** + * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the serial + * number of the it's ancestral work profile. + * + * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)} + * and it corresponds to the profile that was used to restore to the callers profile. + */ + UserHandle getUserForAncestralSerialNumber(in long ancestralSerialNumber); + + /** + * Sets the ancestral work profile for the calling user. + * + * <p> The ancestral work profile corresponds to the profile that was used to restore to the + * callers profile. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + void setAncestralSerialNumber(in long ancestralSerialNumber); + } diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl index cf62e8d625db..76dbf7e4504b 100644 --- a/core/java/android/app/role/IRoleManager.aidl +++ b/core/java/android/app/role/IRoleManager.aidl @@ -32,13 +32,14 @@ interface IRoleManager { List<String> getRoleHoldersAsUser(in String roleName, int userId); - void addRoleHolderAsUser(in String roleName, in String packageName, int userId, + void addRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId, in IRoleManagerCallback callback); - void removeRoleHolderAsUser(in String roleName, in String packageName, int userId, + void removeRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId, in IRoleManagerCallback callback); - void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback); + void clearRoleHoldersAsUser(in String roleName, int flags, int userId, + in IRoleManagerCallback callback); void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId); diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index ddd531339d39..edd3ef983945 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -18,6 +18,7 @@ package android.app.role; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -68,6 +69,22 @@ public final class RoleManager { private static final String LOG_TAG = RoleManager.class.getSimpleName(); /** + * The name of the assistant app role. + * + * @hide + */ + @SystemApi + @TestApi + public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; + + /** + * The name of the browser role. + * + * @see Intent#CATEGORY_APP_BROWSER + */ + public static final String ROLE_BROWSER = "android.app.role.BROWSER"; + + /** * The name of the dialer role. * * @see Intent#ACTION_DIAL @@ -82,18 +99,18 @@ public final class RoleManager { public static final String ROLE_SMS = "android.app.role.SMS"; /** - * The name of the browser role. + * The name of the emergency role * - * @see Intent#CATEGORY_APP_BROWSER + * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE */ - public static final String ROLE_BROWSER = "android.app.role.BROWSER"; + public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY"; /** - * The name of the gallery role. + * The name of the home role. * - * @see Intent#CATEGORY_APP_GALLERY + * @see Intent#CATEGORY_HOME */ - public static final String ROLE_GALLERY = "android.app.role.GALLERY"; + public static final String ROLE_HOME = "android.app.role.HOME"; /** * The name of the music player role. @@ -103,18 +120,11 @@ public final class RoleManager { public static final String ROLE_MUSIC = "android.app.role.MUSIC"; /** - * The name of the home role. - * - * @see Intent#CATEGORY_HOME - */ - public static final String ROLE_HOME = "android.app.role.HOME"; - - /** - * The name of the emergency role + * The name of the gallery role. * - * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE + * @see Intent#CATEGORY_APP_GALLERY */ - public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY"; + public static final String ROLE_GALLERY = "android.app.role.GALLERY"; /** * The name of the car mode dialer app role. @@ -130,20 +140,20 @@ public final class RoleManager { * TODO: STOPSHIP: Make name of required roles public API * @hide */ - public static final String ROLE_CAR_MODE_DIALER_APP = "android.app.role.CAR_MODE_DIALER_APP"; + public static final String ROLE_CAR_MODE_DIALER = "android.app.role.CAR_MODE_DIALER"; /** - * The name of the proxy calling role. + * The name of the call redirection role. * <p> - * A proxy calling app provides a means to re-write the phone number for an outgoing call to - * place the call through a proxy calling service. + * A call redirection app provides a means to re-write the phone number for an outgoing call to + * place the call through a call redirection service. * * @see android.telecom.CallRedirectionService * * TODO: STOPSHIP: Make name of required roles public API * @hide */ - public static final String ROLE_PROXY_CALLING_APP = "android.app.role.PROXY_CALLING_APP"; + public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION"; /** * The name of the call screening and caller id role. @@ -153,7 +163,7 @@ public final class RoleManager { * TODO: STOPSHIP: Make name of required roles public API * @hide */ - public static final String ROLE_CALL_SCREENING_APP = "android.app.role.CALL_SCREENING_APP"; + public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING"; /** * The name of the call companion app role. @@ -169,16 +179,23 @@ public final class RoleManager { * TODO: STOPSHIP: Make name of required roles public API * @hide */ - public static final String ROLE_CALL_COMPANION_APP = "android.app.role.CALL_COMPANION_APP"; + public static final String ROLE_CALL_COMPANION = "android.app.role.CALL_COMPANION"; /** - * The name of the assistant app role. + * @hide + */ + @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP }) + public @interface ManageHoldersFlags {} + + /** + * Flag parameter for {@link #addRoleHolderAsUser}, {@link #removeRoleHolderAsUser} and + * {@link #clearRoleHoldersAsUser} to indicate that apps should not be killed when changing + * their role holder status. * * @hide */ @SystemApi - @TestApi - public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; + public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; /** * The action used to request user approval of a role for an application. @@ -305,9 +322,9 @@ public final class RoleManager { * * @return a list of package names of the role holders, or an empty list if none. * - * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback) - * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback) - * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback) + * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback) + * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback) + * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, RoleManagerCallback) * * @hide */ @@ -335,13 +352,14 @@ public final class RoleManager { * * @param roleName the name of the role to add the role holder for * @param packageName the package name of the application to add to the role holders + * @param flags optional behavior flags * @param user the user to add the role holder for * @param executor the {@code Executor} to run the callback on. * @param callback the callback for whether this call is successful * * @see #getRoleHoldersAsUser(String, UserHandle) - * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback) - * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback) + * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback) + * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, RoleManagerCallback) * * @hide */ @@ -349,15 +367,15 @@ public final class RoleManager { @SystemApi @TestApi public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, - @NonNull RoleManagerCallback callback) { + @ManageHoldersFlags int flags, @NonNull UserHandle user, + @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(user, "user cannot be null"); Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); try { - mService.addRoleHolderAsUser(roleName, packageName, user.getIdentifier(), + mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(), new RoleManagerCallbackDelegate(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -373,13 +391,14 @@ public final class RoleManager { * * @param roleName the name of the role to remove the role holder for * @param packageName the package name of the application to remove from the role holders + * @param flags optional behavior flags * @param user the user to remove the role holder for * @param executor the {@code Executor} to run the callback on. * @param callback the callback for whether this call is successful * * @see #getRoleHoldersAsUser(String, UserHandle) - * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback) - * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback) + * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback) + * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, RoleManagerCallback) * * @hide */ @@ -387,15 +406,15 @@ public final class RoleManager { @SystemApi @TestApi public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, - @NonNull RoleManagerCallback callback) { + @ManageHoldersFlags int flags, @NonNull UserHandle user, + @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(user, "user cannot be null"); Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); try { - mService.removeRoleHolderAsUser(roleName, packageName, user.getIdentifier(), + mService.removeRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(), new RoleManagerCallbackDelegate(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -410,27 +429,29 @@ public final class RoleManager { * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. * * @param roleName the name of the role to remove role holders for + * @param flags optional behavior flags * @param user the user to remove role holders for * @param executor the {@code Executor} to run the callback on. * @param callback the callback for whether this call is successful * * @see #getRoleHoldersAsUser(String, UserHandle) - * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback) - * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback) + * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback) + * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback) * * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi @TestApi - public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user, - @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) { + public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags, + @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, + @NonNull RoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkNotNull(user, "user cannot be null"); Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); try { - mService.clearRoleHoldersAsUser(roleName, user.getIdentifier(), + mService.clearRoleHoldersAsUser(roleName, flags, user.getIdentifier(), new RoleManagerCallbackDelegate(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 47a4a2dca73d..f1bfe8671eec 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -2748,6 +2748,19 @@ public abstract class ContentResolver implements ContentInterface { } /** + * @see #setIsSyncable(Account, String, int) + * @hide + */ + public static void setIsSyncableAsUser(Account account, String authority, int syncable, + int userId) { + try { + getContentService().setIsSyncableAsUser(account, authority, syncable, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Gets the master auto-sync setting that applies to all the providers and accounts. * If this is false then the per-provider auto-sync setting is ignored. * <p>This method requires the caller to hold the permission diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index 1d0237502812..9f6e236306e0 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -126,6 +126,7 @@ interface IContentService { * @param syncable, >0 denotes syncable, 0 means not syncable, <0 means unknown */ void setIsSyncable(in Account account, String providerName, int syncable); + void setIsSyncableAsUser(in Account account, String providerName, int syncable, int userId); void setMasterSyncAutomatically(boolean flag); void setMasterSyncAutomaticallyAsUser(boolean flag, int userId); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index a3021f371e19..d781a96420c7 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -817,28 +817,6 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.action.SHOW_APP_INFO"; /** - * Activity Action: Start an activity to show the app's detailed usage information for - * permission protected data. - * - * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of - * type {@code String[]} and contains the specific permissions to show information for. - * - * Apps should handle this intent if they want to provide more information about permission - * usage to users beyond the information provided in the manifest. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_PERMISSION_USAGE_DETAILS = - "android.intent.action.PERMISSION_USAGE_DETAILS"; - - /** - * The name of the extra used to contain the permissions in - * {@link #ACTION_PERMISSION_USAGE_DETAILS}. - * @see #ACTION_PERMISSION_USAGE_DETAILS - */ - public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS = - "android.intent.extra.PERMISSION_USAGE_PERMISSIONS"; - - /** * Represents a shortcut/live folder icon resource. * * @see Intent#ACTION_CREATE_SHORTCUT diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 6d22277a5384..27a5b392855e 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -204,10 +204,7 @@ public class PackageInfo implements Parcelable { * {@link PackageManager#GET_PERMISSIONS} was set. This list includes * all permissions requested, even those that were not granted or known * by the system at install time. - * - * @deprecated Use {@link #usesPermissions} */ - @Deprecated public String[] requestedPermissions; /** @@ -217,23 +214,10 @@ public class PackageInfo implements Parcelable { * {@link PackageManager#GET_PERMISSIONS} was set. Each value matches * the corresponding entry in {@link #requestedPermissions}, and will have * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate. - * - * @deprecated Use {@link #usesPermissions} */ - @Deprecated public int[] requestedPermissionsFlags; /** - * Array of all {@link android.R.styleable#AndroidManifestUsesPermission - * <uses-permission>} tags included under <manifest>, - * or null if there were none. This is only filled in if the flag - * {@link PackageManager#GET_PERMISSIONS} was set. This list includes - * all permissions requested, even those that were not granted or known - * by the system at install time. - */ - public UsesPermissionInfo[] usesPermissions; - - /** * Flag for {@link #requestedPermissionsFlags}: the requested permission * is required for the application to run; the user can not optionally * disable it. Currently all permissions are required. @@ -480,7 +464,6 @@ public class PackageInfo implements Parcelable { dest.writeTypedArray(permissions, parcelableFlags); dest.writeStringArray(requestedPermissions); dest.writeIntArray(requestedPermissionsFlags); - dest.writeTypedArray(usesPermissions, parcelableFlags); dest.writeTypedArray(signatures, parcelableFlags); dest.writeTypedArray(configPreferences, parcelableFlags); dest.writeTypedArray(reqFeatures, parcelableFlags); @@ -545,7 +528,6 @@ public class PackageInfo implements Parcelable { permissions = source.createTypedArray(PermissionInfo.CREATOR); requestedPermissions = source.createStringArray(); requestedPermissionsFlags = source.createIntArray(); - usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR); signatures = source.createTypedArray(Signature.CREATOR); configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR); reqFeatures = source.createTypedArray(FeatureInfo.CREATOR); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 6e52b33692c8..eaf6c5a9d1cc 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -715,7 +715,6 @@ public abstract class PackageManager { INSTALL_FORCE_PERMISSION_PROMPT, INSTALL_INSTANT_APP, INSTALL_DONT_KILL_APP, - INSTALL_FORCE_SDK, INSTALL_FULL_APP, INSTALL_ALLOCATE_AGGRESSIVE, INSTALL_VIRTUAL_PRELOAD, @@ -816,15 +815,6 @@ public abstract class PackageManager { public static final int INSTALL_DONT_KILL_APP = 0x00001000; /** - * Flag parameter for {@link #installPackage} to indicate that this package is an - * upgrade to a package that refers to the SDK via release letter or is targeting an SDK via - * release letter that the current build does not support. - * - * @hide - */ - public static final int INSTALL_FORCE_SDK = 0x00002000; - - /** * Flag parameter for {@link #installPackage} to indicate that this package is * to be installed as a heavy weight app. This is fundamentally the opposite of * {@link #INSTALL_INSTANT_APP}. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 60449145a740..96b6eb527002 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -791,23 +791,18 @@ public class PackageParser { pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags); } } - N = p.usesPermissionInfos.size(); + N = p.requestedPermissions.size(); if (N > 0) { pi.requestedPermissions = new String[N]; pi.requestedPermissionsFlags = new int[N]; - pi.usesPermissions = new UsesPermissionInfo[N]; for (int i=0; i<N; i++) { - UsesPermissionInfo info = p.usesPermissionInfos.get(i); - final String perm = info.getPermission(); + final String perm = p.requestedPermissions.get(i); pi.requestedPermissions[i] = perm; - int permissionFlags = 0; // The notion of required permissions is deprecated but for compatibility. - permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; + pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; if (grantedPermissions != null && grantedPermissions.contains(perm)) { - permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED; + pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED; } - pi.requestedPermissionsFlags[i] = permissionFlags; - pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags); } } } @@ -844,7 +839,6 @@ public class PackageParser { public static final int PARSE_IS_SYSTEM_DIR = 1 << 4; public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5; public static final int PARSE_ENFORCE_CODE = 1 << 6; - public static final int PARSE_FORCE_SDK = 1 << 7; public static final int PARSE_CHATTY = 1 << 31; @IntDef(flag = true, prefix = { "PARSE_" }, value = { @@ -852,7 +846,6 @@ public class PackageParser { PARSE_COLLECT_CERTIFICATES, PARSE_ENFORCE_CODE, PARSE_EXTERNAL_STORAGE, - PARSE_FORCE_SDK, PARSE_IGNORE_PROCESSES, PARSE_IS_SYSTEM_DIR, PARSE_MUST_BE_APK, @@ -2175,12 +2168,12 @@ public class PackageParser { return null; } } else if (tagName.equals(TAG_USES_PERMISSION)) { - if (!parseUsesPermission(pkg, res, parser, outError)) { + if (!parseUsesPermission(pkg, res, parser)) { return null; } } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M) || tagName.equals(TAG_USES_PERMISSION_SDK_23)) { - if (!parseUsesPermission(pkg, res, parser, outError)) { + if (!parseUsesPermission(pkg, res, parser)) { return null; } } else if (tagName.equals(TAG_USES_CONFIGURATION)) { @@ -2498,7 +2491,7 @@ public class PackageParser { newPermsMsg.append(' '); } newPermsMsg.append(npi.name); - addRequestedPermission(pkg, npi.name); + pkg.requestedPermissions.add(npi.name); pkg.implicitPermissions.add(npi.name); } } @@ -2519,7 +2512,7 @@ public class PackageParser { for (int in = 0; in < newPerms.size(); in++) { final String perm = newPerms.get(in); if (!pkg.requestedPermissions.contains(perm)) { - addRequestedPermission(pkg, perm); + pkg.requestedPermissions.add(perm); pkg.implicitPermissions.add(perm); } } @@ -2599,13 +2592,13 @@ public class PackageParser { } } else { if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) { - addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO); + pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO); } if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) { - addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO); + pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO); } if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) { - addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES); + pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES); } } @@ -2645,12 +2638,6 @@ public class PackageParser { } /** - * Helper method for adding a requested permission to a package outside of a uses-permission. - */ - private void addRequestedPermission(Package pkg, String permission) { - pkg.requestedPermissions.add(permission); - pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission)); - } /** * Matches a given {@code targetCode} against a set of release codeNames. Target codes can @@ -2695,8 +2682,6 @@ public class PackageParser { * @param platformSdkCodenames array of allowed pre-release SDK codenames * for this platform * @param outError output array to populate with error, if applicable - * @param forceCurrentDev if development target code is not available, use the current - * development version by default. * @return the targetSdkVersion to use at runtime, or -1 if the package is * not compatible with this platform * @hide Exposed for unit testing only. @@ -2704,7 +2689,7 @@ public class PackageParser { @TestApi public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers, @Nullable String targetCode, @NonNull String[] platformSdkCodenames, - @NonNull String[] outError, boolean forceCurrentDev) { + @NonNull String[] outError) { // If it's a release SDK, return the version number unmodified. if (targetCode == null) { return targetVers; @@ -2712,7 +2697,7 @@ public class PackageParser { // If it's a pre-release SDK and the codename matches this platform, it // definitely targets this SDK. - if (matchTargetCode(platformSdkCodenames, targetCode) || forceCurrentDev) { + if (matchTargetCode(platformSdkCodenames, targetCode)) { return Build.VERSION_CODES.CUR_DEVELOPMENT; } @@ -2779,9 +2764,8 @@ public class PackageParser { return null; } - boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0; final int targetSdkVersion = computeTargetSdkVersion(targetVers, - targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch); + targetCode, SDK_CODENAMES, outError); if (targetSdkVersion < 0) { return null; } @@ -2987,8 +2971,8 @@ public class PackageParser { return certSha256Digests; } - private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser, - String[] outError) throws XmlPullParserException, IOException { + private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesPermission); @@ -3012,44 +2996,6 @@ public class PackageParser { final String requiredNotfeature = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0); - int dataSentOffDevice = sa.getInt( - com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0); - - int dataSharedWithThirdParty = sa.getInt( - com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0); - - int dataUsedForMonetization = sa.getInt( - com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0); - - int retentionWeeks = -1; - int retention; - - String rawRetention = sa.getString( - com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime); - - if (rawRetention == null) { - retention = UsesPermissionInfo.RETENTION_UNDEFINED; - } else if ("notRetained".equals(rawRetention)) { - retention = UsesPermissionInfo.RETENTION_NOT_RETAINED; - } else if ("userSelected".equals(rawRetention)) { - retention = UsesPermissionInfo.RETENTION_USER_SELECTED; - } else if ("unlimited".equals(rawRetention)) { - retention = UsesPermissionInfo.RETENTION_UNLIMITED; - } else { - // A number of weeks was specified - retention = UsesPermissionInfo.RETENTION_SPECIFIED; - retentionWeeks = sa.getInt( - com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime, - -1); - - if (retentionWeeks < 0) { - outError[0] = "Bad value provided for dataRetentionTime."; - mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - XmlUtils.skipCurrentTag(parser); - sa.recycle(); - return false; - } - } sa.recycle(); XmlUtils.skipCurrentTag(parser); @@ -3082,10 +3028,6 @@ public class PackageParser { + parser.getPositionDescription()); } - UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice, - dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks); - pkg.usesPermissionInfos.add(info); - return true; } @@ -3420,10 +3362,6 @@ public class PackageParser { perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); - perm.info.usageInfoRequired = sa.getInt( - com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0) - != 0; - sa.recycle(); if (perm.info.protectionLevel == -1) { @@ -5260,6 +5198,10 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions, false); + p.info.forceUriPermissions = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestProvider_forceUriPermissions, + false); + p.info.multiprocess = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_multiprocess, false); @@ -6621,9 +6563,6 @@ public class PackageParser { @UnsupportedAppUsage public final ArrayList<String> requestedPermissions = new ArrayList<String>(); - public final ArrayList<UsesPermissionInfo> usesPermissionInfos = - new ArrayList<>(); - /** Permissions requested but not in the manifest. */ public final ArrayList<String> implicitPermissions = new ArrayList<>(); @@ -7155,7 +7094,6 @@ public class PackageParser { dest.readStringList(requestedPermissions); internStringArrayList(requestedPermissions); - dest.readParcelableList(usesPermissionInfos, boot); dest.readStringList(implicitPermissions); internStringArrayList(implicitPermissions); protectedBroadcasts = dest.createStringArrayList(); @@ -7323,7 +7261,6 @@ public class PackageParser { dest.writeParcelableList(instrumentation, flags); dest.writeStringList(requestedPermissions); - dest.writeParcelableList(usesPermissionInfos, flags); dest.writeStringList(implicitPermissions); dest.writeStringList(protectedBroadcasts); diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index fb63e0dad9b6..e24523406ab8 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -368,12 +367,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public CharSequence nonLocalizedDescription; - /** - * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em> - * include permission data usage information in order to be able to be granted this permission. - */ - public boolean usageInfoRequired; - /** @hide */ public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -475,7 +468,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = orig.descriptionRes; requestRes = orig.requestRes; nonLocalizedDescription = orig.nonLocalizedDescription; - usageInfoRequired = orig.usageInfoRequired; } /** @@ -540,7 +532,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(requestRes); TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags); - dest.writeInt(usageInfoRequired ? 1 : 0); } /** @hide */ @@ -581,6 +572,5 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); requestRes = source.readInt(); nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); - usageInfoRequired = source.readInt() != 0; } } diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java index 379b7833150c..f06a628f1913 100644 --- a/core/java/android/content/pm/ProviderInfo.java +++ b/core/java/android/content/pm/ProviderInfo.java @@ -47,7 +47,13 @@ public final class ProviderInfo extends ComponentInfo * grantUriPermissions} attribute. */ public boolean grantUriPermissions = false; - + + /** If true, always apply URI permission grants, as per the + * {@link android.R.styleable#AndroidManifestProvider_forceUriPermissions + * forceUriPermissions} attribute. + */ + public boolean forceUriPermissions = false; + /** * If non-null, these are the patterns that are allowed for granting URI * permissions. Any URI that does not match one of these patterns will not @@ -112,6 +118,7 @@ public final class ProviderInfo extends ComponentInfo readPermission = orig.readPermission; writePermission = orig.writePermission; grantUriPermissions = orig.grantUriPermissions; + forceUriPermissions = orig.forceUriPermissions; uriPermissionPatterns = orig.uriPermissionPatterns; pathPermissions = orig.pathPermissions; multiprocess = orig.multiprocess; @@ -142,6 +149,7 @@ public final class ProviderInfo extends ComponentInfo out.writeString(readPermission); out.writeString(writePermission); out.writeInt(grantUriPermissions ? 1 : 0); + out.writeInt(forceUriPermissions ? 1 : 0); out.writeTypedArray(uriPermissionPatterns, parcelableFlags); out.writeTypedArray(pathPermissions, parcelableFlags); out.writeInt(multiprocess ? 1 : 0); @@ -171,6 +179,7 @@ public final class ProviderInfo extends ComponentInfo readPermission = in.readString(); writePermission = in.readString(); grantUriPermissions = in.readInt() != 0; + forceUriPermissions = in.readInt() != 0; uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR); pathPermissions = in.createTypedArray(PathPermission.CREATOR); multiprocess = in.readInt() != 0; diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java deleted file mode 100644 index d08548fa31a5..000000000000 --- a/core/java/android/content/pm/UsesPermissionInfo.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm; - -import android.annotation.IntDef; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.RetentionPolicy; -/** - * Information you can retrive about a particular application requested permission. This - * corresponds to information collected from the AndroidManifest.xml's <uses-permission> - * tags. - */ -public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable { - - /** - * Flag for {@link #getFlags()}: the requested permission is currently granted to the - * application. - */ - public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1; - - /** @hide */ - @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED}) - @java.lang.annotation.Retention(RetentionPolicy.SOURCE) - public @interface Flags {} - - /** An unset value for {@link #getDataSentOffDevice()}, - * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()} - */ - public static final int USAGE_UNDEFINED = 0; - - /** - * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()}, - * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of - * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty}, - * and {@link android.R.attr#dataUsedForMonetization} attributes. - */ - public static final int USAGE_YES = 1; - - /** - * A user triggered only value for {@link #getDataSentOffDevice()}, - * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()} - * corresponding to the <code>userTriggered</code> value of - * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty}, - * and {@link android.R.attr#dataUsedForMonetization} attributes. - */ - public static final int USAGE_USER_TRIGGERED = 2; - - /** - * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()}, - * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of - * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty}, - * and {@link android.R.attr#dataUsedForMonetization} attributes. - */ - public static final int USAGE_NO = 3; - - /** @hide */ - @IntDef(prefix = {"USAGE_"}, value = { - USAGE_UNDEFINED, - USAGE_YES, - USAGE_USER_TRIGGERED, - USAGE_NO}) - @java.lang.annotation.Retention(RetentionPolicy.SOURCE) - public @interface Usage {} - - /** - * An unset value for {@link #getDataRetention}. - */ - public static final int RETENTION_UNDEFINED = 0; - - /** - * A data not retained value for {@link #getDataRetention()} corresponding to the - * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}. - */ - public static final int RETENTION_NOT_RETAINED = 1; - - /** - * A user selected value for {@link #getDataRetention()} corresponding to the - * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}. - */ - public static final int RETENTION_USER_SELECTED = 2; - - /** - * An unlimited value for {@link #getDataRetention()} corresponding to the - * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}. - */ - public static final int RETENTION_UNLIMITED = 3; - - /** - * A specified value for {@link #getDataRetention()} corresponding to providing the number of - * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks - * is available in {@link #getDataRetentionWeeks()}. - */ - public static final int RETENTION_SPECIFIED = 4; - - /** @hide */ - @IntDef(prefix = {"RETENTION_"}, value = { - RETENTION_UNDEFINED, - RETENTION_NOT_RETAINED, - RETENTION_USER_SELECTED, - RETENTION_UNLIMITED, - RETENTION_SPECIFIED}) - @java.lang.annotation.Retention(RetentionPolicy.SOURCE) - public @interface Retention {} - - private final String mPermission; - private final @Flags int mFlags; - private final @Usage int mDataSentOffDevice; - private final @Usage int mDataSharedWithThirdParty; - private final @Usage int mDataUsedForMonetization; - private final @Retention int mDataRetention; - private final int mDataRetentionWeeks; - - /** @hide */ - public UsesPermissionInfo(String permission) { - mPermission = permission; - mDataSentOffDevice = USAGE_UNDEFINED; - mDataSharedWithThirdParty = USAGE_UNDEFINED; - mDataUsedForMonetization = USAGE_UNDEFINED; - mDataRetention = RETENTION_UNDEFINED; - mDataRetentionWeeks = -1; - mFlags = 0; - } - - /** @hide */ - public UsesPermissionInfo(String permission, - @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty, - @Usage int dataUsedForMonetization, @Retention int dataRetention, - int dataRetentionWeeks) { - mPermission = permission; - mDataSentOffDevice = dataSentOffDevice; - mDataSharedWithThirdParty = dataSharedWithThirdParty; - mDataUsedForMonetization = dataUsedForMonetization; - mDataRetention = dataRetention; - mDataRetentionWeeks = dataRetentionWeeks; - mFlags = 0; - } - - /** @hide */ - public UsesPermissionInfo(UsesPermissionInfo orig) { - this(orig, orig.mFlags); - } - - /** @hide */ - public UsesPermissionInfo(UsesPermissionInfo orig, int flags) { - super(orig); - mPermission = orig.mPermission; - mFlags = flags; - mDataSentOffDevice = orig.mDataSentOffDevice; - mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty; - mDataUsedForMonetization = orig.mDataUsedForMonetization; - mDataRetention = orig.mDataRetention; - mDataRetentionWeeks = orig.mDataRetentionWeeks; - } - - /** - * The name of the requested permission. - */ - public String getPermission() { - return mPermission; - } - - public @Flags int getFlags() { - return mFlags; - } - - /** - * If the application sends the data guarded by this permission off the device. - * - * See {@link android.R.attr#dataSentOffDevice} - */ - public @Usage int getDataSentOffDevice() { - return mDataSentOffDevice; - } - - /** - * If the application or its services shares the data guarded by this permission with third - * parties. - * - * See {@link android.R.attr#dataSharedWithThirdParty} - */ - public @Usage int getDataSharedWithThirdParty() { - return mDataSharedWithThirdParty; - } - - /** - * If the application or its services use the data guarded by this permission for monetization - * purposes. - * - * See {@link android.R.attr#dataUsedForMonetization} - */ - public @Usage int getDataUsedForMonetization() { - return mDataUsedForMonetization; - } - - /** - * How long the application or its services store the data guarded by this permission. - * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the - * number of weeks the data is stored. - * - * See {@link android.R.attr#dataRetentionTime} - */ - public @Retention int getDataRetention() { - return mDataRetention; - } - - /** - * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the - * application or its services store data guarded by this permission. - * - * @throws IllegalStateException if {@link #getDataRetention} is not - * {@link #RETENTION_SPECIFIED}. - */ - public int getDataRetentionWeeks() { - if (mDataRetention != RETENTION_SPECIFIED) { - throw new IllegalStateException("Data retention weeks not specified"); - } - return mDataRetentionWeeks; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeString(mPermission); - dest.writeInt(mFlags); - dest.writeInt(mDataSentOffDevice); - dest.writeInt(mDataSharedWithThirdParty); - dest.writeInt(mDataUsedForMonetization); - dest.writeInt(mDataRetention); - dest.writeInt(mDataRetentionWeeks); - } - - private UsesPermissionInfo(Parcel source) { - super(source); - mPermission = source.readString(); - mFlags = source.readInt(); - mDataSentOffDevice = source.readInt(); - mDataSharedWithThirdParty = source.readInt(); - mDataUsedForMonetization = source.readInt(); - mDataRetention = source.readInt(); - mDataRetentionWeeks = source.readInt(); - } - - public static final Creator<UsesPermissionInfo> CREATOR = - new Creator<UsesPermissionInfo>() { - @Override - public UsesPermissionInfo createFromParcel(Parcel source) { - return new UsesPermissionInfo(source); - } - @Override - public UsesPermissionInfo[] newArray(int size) { - return new UsesPermissionInfo[size]; - } - }; -} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 9e0a9ba0dc7b..49b4cb01c6a6 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -47,6 +47,7 @@ import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; /** * Provides access to an application's raw asset files; see {@link Resources} @@ -1051,6 +1052,14 @@ public final class AssetManager implements AutoCloseable { } } + int[] getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, @StyleRes int xmlStyle) { + synchronized (this) { + return nativeAttributeResolutionStack( + mObject, themePtr, xmlStyle, defStyleAttr, defStyleRes); + } + } + @UnsupportedAppUsage boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, @@ -1337,6 +1346,17 @@ public final class AssetManager implements AutoCloseable { } } + /** + * @hide + */ + @GuardedBy("this") + public @Nullable Map<String, String> getOverlayableMap(String packageName) { + synchronized (this) { + ensureValidLocked(); + return nativeGetOverlayableMap(mObject, packageName); + } + } + @GuardedBy("this") private void incRefsLocked(long id) { if (DEBUG_REFS) { @@ -1419,6 +1439,8 @@ public final class AssetManager implements AutoCloseable { private static native @Nullable String nativeGetLastResourceResolution(long ptr); // Style attribute retrieval native methods. + private static native int[] nativeAttributeResolutionStack(long ptr, long themePtr, + @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes); private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, long outValuesAddress, long outIndicesAddress); @@ -1452,6 +1474,8 @@ public final class AssetManager implements AutoCloseable { private static native void nativeVerifySystemIdmaps(); private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid(); + private static native @Nullable Map nativeGetOverlayableMap(long ptr, + @NonNull String packageName); // Global debug native methods. /** diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 59db49e0d37e..a2ae994f7b0f 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1725,6 +1725,68 @@ public class Resources { public void rebase() { mThemeImpl.rebase(); } + + /** + * Returns the resource ID for the style specified using {@code style="..."} in the + * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise if not + * specified or otherwise not applicable. + * <p> + * Each {@link android.view.View} can have an explicit style specified in the layout file. + * This style is used first during the {@link android.view.View} attribute resolution, then + * if an attribute is not defined there the resource system looks at default style and theme + * as fallbacks. + * + * @param set The base set of attribute values. + * + * @return The resource ID for the style specified using {@code style="..."} in the + * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise + * if not specified or otherwise not applicable. + */ + @StyleRes + public int getExplicitStyle(@Nullable AttributeSet set) { + if (set == null) { + return ID_NULL; + } + int styleAttr = set.getStyleAttribute(); + if (styleAttr == ID_NULL) { + return ID_NULL; + } + String styleAttrType = getResources().getResourceTypeName(styleAttr); + if ("attr".equals(styleAttrType)) { + TypedValue explicitStyle = new TypedValue(); + boolean resolved = resolveAttribute(styleAttr, explicitStyle, true); + if (resolved) { + return explicitStyle.resourceId; + } + } else if ("style".equals(styleAttrType)) { + return styleAttr; + } + return ID_NULL; + } + + /** + * Returns the ordered list of resource ID that are considered when resolving attribute + * values when making an equivalent call to + * {@link #obtainStyledAttributes(AttributeSet, int[], int, int)} . The list will include + * a set of explicit styles ({@code explicitStyleRes} and it will include the default styles + * ({@code defStyleAttr} and {@code defStyleRes}). + * + * @param defStyleAttr An attribute in the current theme that contains a + * reference to a style resource that supplies + * defaults values for the TypedArray. Can be + * 0 to not look for defaults. + * @param defStyleRes A resource identifier of a style resource that + * supplies default values for the TypedArray, + * used only if defStyleAttr is 0 or can not be found + * in the theme. Can be 0 to not look for defaults. + * @param explicitStyleRes A resource identifier of an explicit style resource. + * @return ordered list of resource ID that are considered when resolving attribute values. + */ + public int[] getAttributeResolutionStack(@AttrRes int defStyleAttr, + @StyleRes int defStyleRes, @StyleRes int explicitStyleRes) { + return mThemeImpl.getAttributeResolutionStack( + defStyleAttr, defStyleRes, explicitStyleRes); + } } static class ThemeKey implements Cloneable { diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 98980799a365..da064c956fcc 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -1488,6 +1488,32 @@ public class ResourcesImpl { } } } + + /** + * Returns the ordered list of resource ID that are considered when resolving attribute + * values when making an equivalent call to + * {@link #obtainStyledAttributes(Resources.Theme, AttributeSet, int[], int, int)}. The list + * will include a set of explicit styles ({@code explicitStyleRes} and it will include the + * default styles ({@code defStyleAttr} and {@code defStyleRes}). + * + * @param defStyleAttr An attribute in the current theme that contains a + * reference to a style resource that supplies + * defaults values for the TypedArray. Can be + * 0 to not look for defaults. + * @param defStyleRes A resource identifier of a style resource that + * supplies default values for the TypedArray, + * used only if defStyleAttr is 0 or can not be found + * in the theme. Can be 0 to not look for defaults. + * @param explicitStyleRes A resource identifier of an explicit style resource. + * @return ordered list of resource ID that are considered when resolving attribute values. + */ + public int[] getAttributeResolutionStack(@AttrRes int defStyleAttr, + @StyleRes int defStyleRes, @StyleRes int explicitStyleRes) { + synchronized (mKey) { + return mAssets.getAttributeResolutionStack( + mTheme, defStyleAttr, defStyleRes, explicitStyleRes); + } + } } private static class LookupStack { diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl index d951756bb513..db9fcb6abdfa 100644 --- a/core/java/android/content/rollback/IRollbackManager.aidl +++ b/core/java/android/content/rollback/IRollbackManager.aidl @@ -47,4 +47,8 @@ interface IRollbackManager { // // NOTE: This call is synchronous. boolean notifyStagedSession(int sessionId); + + // Used by the staging manager to notify the RollbackManager of the apk + // session for a staged session. + void notifyStagedApkSession(int originalSessionId, int apkSessionId); } diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java index 0ec40183c319..1d0ab5ad2679 100644 --- a/core/java/android/content/rollback/PackageRollbackInfo.java +++ b/core/java/android/content/rollback/PackageRollbackInfo.java @@ -22,6 +22,7 @@ import android.content.pm.VersionedPackage; import android.os.Parcel; import android.os.Parcelable; import android.util.IntArray; +import android.util.SparseLongArray; import java.util.ArrayList; @@ -73,6 +74,18 @@ public final class PackageRollbackInfo implements Parcelable { */ private final boolean mIsApex; + /* + * The list of users the package is installed for. + */ + // NOTE: Not a part of the Parcelable representation of this object. + private final IntArray mInstalledUsers; + + /** + * A mapping between user and an inode of theirs CE data snapshot. + */ + // NOTE: Not a part of the Parcelable representation of this object. + private final SparseLongArray mCeSnapshotInodes; + /** * Returns the name of the package to roll back from. */ @@ -126,15 +139,33 @@ public final class PackageRollbackInfo implements Parcelable { } /** @hide */ + public IntArray getInstalledUsers() { + return mInstalledUsers; + } + + /** @hide */ + public SparseLongArray getCeSnapshotInodes() { + return mCeSnapshotInodes; + } + + /** @hide */ + public void putCeSnapshotInode(int userId, long ceSnapshotInode) { + mCeSnapshotInodes.put(userId, ceSnapshotInode); + } + + /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo, @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores, - boolean isApex) { + boolean isApex, @NonNull IntArray installedUsers, + @NonNull SparseLongArray ceSnapshotInodes) { this.mVersionRolledBackFrom = packageRolledBackFrom; this.mVersionRolledBackTo = packageRolledBackTo; this.mPendingBackups = pendingBackups; this.mPendingRestores = pendingRestores; this.mIsApex = isApex; + this.mInstalledUsers = installedUsers; + this.mCeSnapshotInodes = ceSnapshotInodes; } private PackageRollbackInfo(Parcel in) { @@ -143,6 +174,8 @@ public final class PackageRollbackInfo implements Parcelable { this.mIsApex = in.readBoolean(); this.mPendingRestores = null; this.mPendingBackups = null; + this.mInstalledUsers = null; + this.mCeSnapshotInodes = null; } @Override diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java index 4469f0f965c3..51eb7fc2d804 100644 --- a/core/java/android/debug/AdbManagerInternal.java +++ b/core/java/android/debug/AdbManagerInternal.java @@ -16,6 +16,8 @@ package android.debug; +import java.io.File; + /** * This class allows the control of ADB-related functions that should only be called from the system * server. @@ -41,4 +43,14 @@ public abstract class AdbManagerInternal { * Returns {@code true} if ADB debugging is enabled. */ public abstract boolean isAdbEnabled(); + + /** + * Returns the file that contains all of the ADB keys used by the device. + */ + public abstract File getAdbKeysFile(); + + /** + * Returns the file that contains all of the ADB keys and their last used time. + */ + public abstract File getAdbTempKeysFile(); } diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index c814b7c67817..1cb7eb0d1256 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -17,6 +17,7 @@ package android.hardware.biometrics; import android.annotation.UnsupportedAppUsage; +import android.app.KeyguardManager; /** @@ -126,6 +127,13 @@ public interface BiometricConstants { int BIOMETRIC_ERROR_NEGATIVE_BUTTON = 13; /** + * The device does not have pin, pattern, or password set up. See + * {@link BiometricPrompt.Builder#setAllowDeviceCredential(boolean)} and + * {@link KeyguardManager#isDeviceSecure()} + */ + int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; + + /** * @hide */ @UnsupportedAppUsage diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index b708ef12b210..459ec62d4135 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -16,6 +16,7 @@ package android.hardware.biometrics; +import android.app.KeyguardManager; import android.hardware.face.FaceManager; /** @@ -134,6 +135,13 @@ public interface BiometricFaceConstants { public static final int FACE_ERROR_NEGATIVE_BUTTON = 13; /** + * The device does not have pin, pattern, or password set up. See + * {@link BiometricPrompt.Builder#setAllowDeviceCredential(boolean)} and + * {@link KeyguardManager#isDeviceSecure()} + */ + public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; + + /** * @hide */ public static final int FACE_ERROR_VENDOR_BASE = 1000; diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 041b2e673b96..6cbab471ce23 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -17,6 +17,7 @@ package android.hardware.biometrics; import android.annotation.UnsupportedAppUsage; +import android.app.KeyguardManager; import android.hardware.fingerprint.FingerprintManager; /** @@ -119,6 +120,14 @@ public interface BiometricFingerprintConstants { public static final int FINGERPRINT_ERROR_NEGATIVE_BUTTON = 13; /** + * The device does not have pin, pattern, or password set up. See + * {@link BiometricPrompt.Builder#setAllowDeviceCredential(boolean)} and + * {@link KeyguardManager#isDeviceSecure()} + * @hide + */ + public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; + + /** * @hide */ @UnsupportedAppUsage diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index d569a7800c37..baf972b26573 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -23,6 +23,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.app.KeyguardManager; import android.content.Context; import android.content.DialogInterface; import android.os.Binder; @@ -80,7 +81,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * @hide */ - public static final String KEY_ENABLE_FALLBACK = "enable_fallback"; + public static final String KEY_ALLOW_DEVICE_CREDENTIAL = "allow_device_credential"; /** * Error/help message will show for this amount of time. @@ -203,7 +204,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * "Cancel" button, but may be also used to show an alternative method for authentication, * such as screen that asks for a backup password. * - * Note that this should not be set if {@link #setEnableFallback(boolean)} is set to true. + * Note that this should not be set if {@link #setAllowDeviceCredential(boolean) + * is set to true. * * @param text * @return @@ -250,7 +252,10 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * The user will first be prompted to authenticate with biometrics, but also given the - * option to authenticate with their device PIN, pattern, or password. + * option to authenticate with their device PIN, pattern, or password. Developers should + * first check {@link KeyguardManager#isDeviceSecure()} before enabling this. If the device + * is not secure, {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL} will be + * returned in {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}} * * Note that {@link #setNegativeButton(CharSequence, Executor, * DialogInterface.OnClickListener)} should not be set if this is set to true. @@ -259,8 +264,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * credentials (PIN, pattern, or password). * @return */ - public Builder setEnableFallback(boolean enable) { - mBundle.putBoolean(KEY_ENABLE_FALLBACK, enable); + public Builder setAllowDeviceCredential(boolean enable) { + mBundle.putBoolean(KEY_ALLOW_DEVICE_CREDENTIAL, enable); return this; } @@ -273,7 +278,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan final CharSequence title = mBundle.getCharSequence(KEY_TITLE); final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT); final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE); - final boolean enableFallback = mBundle.getBoolean(KEY_ENABLE_FALLBACK); + final boolean enableFallback = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL); if (TextUtils.isEmpty(title) && !useDefaultTitle) { throw new IllegalArgumentException("Title must be set and non-empty"); @@ -281,7 +286,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalArgumentException("Negative text must be set and non-empty"); } else if (!TextUtils.isEmpty(negative) && enableFallback) { throw new IllegalArgumentException("Can't have both negative button behavior" - + " and fallback enabled"); + + " and device credential enabled"); } return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo); } @@ -541,8 +546,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - if (mBundle.getBoolean(KEY_ENABLE_FALLBACK)) { - throw new IllegalArgumentException("Fallback not supported with crypto"); + if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)) { + throw new IllegalArgumentException("Device credential not supported with crypto"); } authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId()); } diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index e4336d171ab6..a20e2bf3d067 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -54,7 +54,7 @@ interface IBiometricService { // TODO(b/123378871): Remove when moved. // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's - // setEnableFallback method, since there's no way for us to intercept onActivityResult. + // setAllowDeviceCredential method, since there's no way for us to intercept onActivityResult. // CDCA is launched from BiometricService (startActivityAsUser) instead of *ForResult. void onConfirmDeviceCredentialSuccess(); // TODO(b/123378871): Remove when moved. diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 1881a0cd32e8..0e4ff78af1e0 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1159,9 +1159,10 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>Each output JPEG size in android.scaler.availableStreamConfigurations will have at least * one corresponding size that has the same aspect ratio in availableThumbnailSizes, * and vice versa.</li> - * <li>All non-<code>(0, 0)</code> sizes will have non-zero widths and heights. - * This key is available on all devices.</li> + * <li>All non-<code>(0, 0)</code> sizes will have non-zero widths and heights.</li> * </ul> + * <p>This list is also used as supported thumbnail sizes for HEIC image format capture.</p> + * <p>This key is available on all devices.</p> * * @see CaptureRequest#JPEG_THUMBNAIL_SIZE */ @@ -3838,6 +3839,74 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri public static final Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES = new Key<int[]>("android.distortionCorrection.availableModes", int[].class); + /** + * <p>The available HEIC (ISO/IEC 23008-12) stream + * configurations that this camera device supports + * (i.e. format, width, height, output/input stream).</p> + * <p>The configurations are listed as <code>(format, width, height, input?)</code> tuples.</p> + * <p>If the camera device supports HEIC image format, it will support identical set of stream + * combinations involving HEIC image format, compared to the combinations involving JPEG + * image format as required by the device's hardware level and capabilities.</p> + * <p>All the static, control, and dynamic metadata tags related to JPEG apply to HEIC formats. + * Configuring JPEG and HEIC streams at the same time is not supported.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS = + new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class); + + /** + * <p>This lists the minimum frame duration for each + * format/size combination for HEIC output formats.</p> + * <p>This should correspond to the frame duration when only that + * stream is active, with all processing (typically in android.*.mode) + * set to either OFF or FAST.</p> + * <p>When multiple streams are used in a request, the minimum frame + * duration will be max(individual stream min durations).</p> + * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and + * android.scaler.availableStallDurations for more details about + * calculating the max frame rate.</p> + * <p><b>Units</b>: (format, width, height, ns) x n</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS = + new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); + + /** + * <p>This lists the maximum stall duration for each + * output format/size combination for HEIC streams.</p> + * <p>A stall duration is how much extra time would get added + * to the normal minimum frame duration for a repeating request + * that has streams with non-zero stall.</p> + * <p>This functions similarly to + * android.scaler.availableStallDurations for HEIC + * streams.</p> + * <p>All HEIC output stream formats may have a nonzero stall + * duration.</p> + * <p><b>Units</b>: (format, width, height, ns) x n</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_STALL_DURATIONS = + new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 9c213f2f27a5..20fc53fee2e6 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -356,12 +356,6 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} - * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices - * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV} - * streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level - * and capabilities.</p> - * * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices * support at least the following stream combinations in addition to those for @@ -435,6 +429,18 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} + * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices + * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV} + * streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level + * and capabilities.</p> + * + * <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats} + * contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG} + * streams with {@code HEIC} in all guaranteed stream combinations for the device's hardware + * level and capabilities. Calling createCaptureSession with both JPEG and HEIC outputs is not + * supported.</p> + * * <p>Clients can access the above mandatory stream combination tables via * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p> * diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 3d3a916bae7a..525070103c2c 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2126,6 +2126,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>Setting a location object in a request will include the GPS coordinates of the location * into any JPEG images captured based on the request. These coordinates can then be * viewed by anyone who receives the JPEG image.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p>This key is available on all devices.</p> */ @PublicKey @@ -2136,6 +2137,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>GPS coordinates to include in output JPEG * EXIF.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p><b>Range of valid values:</b><br> * (-180 - 180], [-90,90], [-inf, inf]</p> * <p>This key is available on all devices.</p> @@ -2147,6 +2149,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>32 characters describing GPS algorithm to * include in EXIF.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p>This key is available on all devices.</p> * @hide */ @@ -2156,6 +2159,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Time GPS fix was made to include in * EXIF.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p><b>Units</b>: UTC in seconds since January 1, 1970</p> * <p>This key is available on all devices.</p> * @hide @@ -2195,6 +2199,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * </code></pre> * <p>For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will * also be set to EXTERNAL. The above code is not relevant in such case.</p> + * <p>This tag is also used to describe the orientation of the HEIC image capture, in which + * case the rotation is reflected by + * {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag}, and not by + * rotating the image data itself.</p> * <p><b>Units</b>: Degrees in multiples of 90</p> * <p><b>Range of valid values:</b><br> * 0, 90, 180, 270</p> @@ -2209,7 +2217,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Compression quality of the final JPEG * image.</p> - * <p>85-95 is typical usage range.</p> + * <p>85-95 is typical usage range. This tag is also used to describe the quality + * of the HEIC image capture.</p> * <p><b>Range of valid values:</b><br> * 1-100; larger is higher quality</p> * <p>This key is available on all devices.</p> @@ -2221,6 +2230,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Compression quality of JPEG * thumbnail.</p> + * <p>This tag is also used to describe the quality of the HEIC image capture.</p> * <p><b>Range of valid values:</b><br> * 1-100; larger is higher quality</p> * <p>This key is available on all devices.</p> @@ -2253,6 +2263,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * orientation is requested. LEGACY device will always report unrotated thumbnail * size.</li> * </ul> + * <p>The tag is also used as thumbnail size for HEIC image format capture, in which case the + * the thumbnail rotation is reflected by + * {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag}, and not by + * rotating the thumbnail data itself.</p> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#JPEG_AVAILABLE_THUMBNAIL_SIZES android.jpeg.availableThumbnailSizes}</p> * <p>This key is available on all devices.</p> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 8982b40be29b..13ad092f6efd 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2450,6 +2450,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>Setting a location object in a request will include the GPS coordinates of the location * into any JPEG images captured based on the request. These coordinates can then be * viewed by anyone who receives the JPEG image.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p>This key is available on all devices.</p> */ @PublicKey @@ -2460,6 +2461,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>GPS coordinates to include in output JPEG * EXIF.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p><b>Range of valid values:</b><br> * (-180 - 180], [-90,90], [-inf, inf]</p> * <p>This key is available on all devices.</p> @@ -2471,6 +2473,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>32 characters describing GPS algorithm to * include in EXIF.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p>This key is available on all devices.</p> * @hide */ @@ -2480,6 +2483,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Time GPS fix was made to include in * EXIF.</p> + * <p>This tag is also used for HEIC image capture.</p> * <p><b>Units</b>: UTC in seconds since January 1, 1970</p> * <p>This key is available on all devices.</p> * @hide @@ -2519,6 +2523,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * </code></pre> * <p>For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will * also be set to EXTERNAL. The above code is not relevant in such case.</p> + * <p>This tag is also used to describe the orientation of the HEIC image capture, in which + * case the rotation is reflected by + * {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag}, and not by + * rotating the image data itself.</p> * <p><b>Units</b>: Degrees in multiples of 90</p> * <p><b>Range of valid values:</b><br> * 0, 90, 180, 270</p> @@ -2533,7 +2541,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Compression quality of the final JPEG * image.</p> - * <p>85-95 is typical usage range.</p> + * <p>85-95 is typical usage range. This tag is also used to describe the quality + * of the HEIC image capture.</p> * <p><b>Range of valid values:</b><br> * 1-100; larger is higher quality</p> * <p>This key is available on all devices.</p> @@ -2545,6 +2554,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Compression quality of JPEG * thumbnail.</p> + * <p>This tag is also used to describe the quality of the HEIC image capture.</p> * <p><b>Range of valid values:</b><br> * 1-100; larger is higher quality</p> * <p>This key is available on all devices.</p> @@ -2577,6 +2587,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * orientation is requested. LEGACY device will always report unrotated thumbnail * size.</li> * </ul> + * <p>The tag is also used as thumbnail size for HEIC image format capture, in which case the + * the thumbnail rotation is reflected by + * {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag}, and not by + * rotating the thumbnail data itself.</p> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#JPEG_AVAILABLE_THUMBNAIL_SIZES android.jpeg.availableThumbnailSizes}</p> * <p>This key is available on all devices.</p> diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 7877a4d51313..65026b6feb9f 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1133,6 +1133,9 @@ public class CameraMetadataNative implements Parcelable { /*dynamicDepthConfigurations*/ null, /*dynamicDepthMinFrameDurations*/ null, /*dynamicDepthStallDurations*/ null, + /*heicconfiguration*/ null, + /*heicminduration*/ null, + /*heicstallduration*/ null, /*highspeedvideoconfigurations*/ null, /*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]); break; @@ -1144,6 +1147,9 @@ public class CameraMetadataNative implements Parcelable { /*dynamicDepthConfigurations*/ null, /*dynamicDepthMinFrameDurations*/ null, /*dynamicDepthStallDurations*/ null, + /*heicconfiguration*/ null, + /*heicminduration*/ null, + /*heicstallduration*/ null, highSpeedVideoConfigurations, /*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]); break; @@ -1155,6 +1161,9 @@ public class CameraMetadataNative implements Parcelable { /*dynamicDepthConfigurations*/ null, /*dynamicDepthMinFrameDurations*/ null, /*dynamicDepthStallDurations*/ null, + /*heicconfiguration*/ null, + /*heicminduration*/ null, + /*heicstallduration*/ null, /*highSpeedVideoConfigurations*/ null, inputOutputFormatsMap, listHighResolution, supportsPrivate[i]); break; @@ -1166,6 +1175,9 @@ public class CameraMetadataNative implements Parcelable { /*dynamicDepthConfigurations*/ null, /*dynamicDepthMinFrameDurations*/ null, /*dynamicDepthStallDurations*/ null, + /*heicconfiguration*/ null, + /*heicminduration*/ null, + /*heicstallduration*/ null, /*highSpeedVideoConfigurations*/ null, /*inputOutputFormatsMap*/ null, listHighResolution, supportsPrivate[i]); } @@ -1230,6 +1242,12 @@ public class CameraMetadataNative implements Parcelable { CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS); StreamConfigurationDuration[] dynamicDepthStallDurations = getBase( CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS); + StreamConfiguration[] heicConfigurations = getBase( + CameraCharacteristics.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS); + StreamConfigurationDuration[] heicMinFrameDurations = getBase( + CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS); + StreamConfigurationDuration[] heicStallDurations = getBase( + CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS); HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase( CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS); ReprocessFormatsMap inputOutputFormatsMap = getBase( @@ -1239,7 +1257,9 @@ public class CameraMetadataNative implements Parcelable { configurations, minFrameDurations, stallDurations, depthConfigurations, depthMinFrameDurations, depthStallDurations, dynamicDepthConfigurations, dynamicDepthMinFrameDurations, - dynamicDepthStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap, + dynamicDepthStallDurations, heicConfigurations, + heicMinFrameDurations, heicStallDurations, + highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution); } diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index a22e008a65fd..996f9978a612 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -79,6 +79,22 @@ public final class StreamConfigurationMap { * @param configurations a non-{@code null} array of {@link StreamConfiguration} * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration} * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param depthConfigurations a non-{@code null} array of depth {@link StreamConfiguration} + * @param depthMinFrameDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param depthStallDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthConfigurations a non-{@code null} array of dynamic depth + * {@link StreamConfiguration} + * @param dynamicDepthMinFrameDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthStallDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param heicConfigurations a non-{@code null} array of heic {@link StreamConfiguration} + * @param heicMinFrameDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} + * @param heicStallDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if * camera device does not support high speed video recording * @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE @@ -98,14 +114,19 @@ public final class StreamConfigurationMap { StreamConfiguration[] dynamicDepthConfigurations, StreamConfigurationDuration[] dynamicDepthMinFrameDurations, StreamConfigurationDuration[] dynamicDepthStallDurations, + StreamConfiguration[] heicConfigurations, + StreamConfigurationDuration[] heicMinFrameDurations, + StreamConfigurationDuration[] heicStallDurations, HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, ReprocessFormatsMap inputOutputFormatsMap, boolean listHighResolution) { this(configurations, minFrameDurations, stallDurations, depthConfigurations, depthMinFrameDurations, depthStallDurations, dynamicDepthConfigurations, dynamicDepthMinFrameDurations, - dynamicDepthStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap, - listHighResolution, /*enforceImplementationDefined*/ true); + dynamicDepthStallDurations, + heicConfigurations, heicMinFrameDurations, heicStallDurations, + highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution, + /*enforceImplementationDefined*/ true); } /** @@ -117,6 +138,22 @@ public final class StreamConfigurationMap { * @param configurations a non-{@code null} array of {@link StreamConfiguration} * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration} * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param depthConfigurations a non-{@code null} array of depth {@link StreamConfiguration} + * @param depthMinFrameDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param depthStallDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthConfigurations a non-{@code null} array of dynamic depth + * {@link StreamConfiguration} + * @param dynamicDepthMinFrameDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthStallDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param heicConfigurations a non-{@code null} array of heic {@link StreamConfiguration} + * @param heicMinFrameDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} + * @param heicStallDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if * camera device does not support high speed video recording * @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE @@ -138,14 +175,23 @@ public final class StreamConfigurationMap { StreamConfiguration[] dynamicDepthConfigurations, StreamConfigurationDuration[] dynamicDepthMinFrameDurations, StreamConfigurationDuration[] dynamicDepthStallDurations, + StreamConfiguration[] heicConfigurations, + StreamConfigurationDuration[] heicMinFrameDurations, + StreamConfigurationDuration[] heicStallDurations, HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, ReprocessFormatsMap inputOutputFormatsMap, boolean listHighResolution, boolean enforceImplementationDefined) { + if (configurations == null && + depthConfigurations == null && + heicConfigurations == null) { + throw new NullPointerException("At least one of color/depth/heic configurations " + + "must not be null"); + } + if (configurations == null) { // If no color configurations exist, ensure depth ones do - checkArrayElementsNotNull(depthConfigurations, "depthConfigurations"); mConfigurations = new StreamConfiguration[0]; mMinFrameDurations = new StreamConfigurationDuration[0]; mStallDurations = new StreamConfigurationDuration[0]; @@ -183,6 +229,19 @@ public final class StreamConfigurationMap { "dynamicDepthStallDurations"); } + if (heicConfigurations == null) { + mHeicConfigurations = new StreamConfiguration[0]; + mHeicMinFrameDurations = new StreamConfigurationDuration[0]; + mHeicStallDurations = new StreamConfigurationDuration[0]; + } else { + mHeicConfigurations = checkArrayElementsNotNull(heicConfigurations, + "heicConfigurations"); + mHeicMinFrameDurations = checkArrayElementsNotNull(heicMinFrameDurations, + "heicMinFrameDurations"); + mHeicStallDurations = checkArrayElementsNotNull(heicStallDurations, + "heicStallDurations"); + } + if (highSpeedVideoConfigurations == null) { mHighSpeedVideoConfigurations = new HighSpeedVideoConfiguration[0]; } else { @@ -235,6 +294,17 @@ public final class StreamConfigurationMap { mDynamicDepthOutputFormats.get(config.getFormat()) + 1); } + // For each heic format, track how many sizes there are available to configure + for (StreamConfiguration config : mHeicConfigurations) { + if (!config.isOutput()) { + // Ignoring input depth configs + continue; + } + + mHeicOutputFormats.put(config.getFormat(), + mHeicOutputFormats.get(config.getFormat()) + 1); + } + if (configurations != null && enforceImplementationDefined && mOutputFormats.indexOfKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) < 0) { throw new AssertionError( @@ -302,7 +372,16 @@ public final class StreamConfigurationMap { if (mInputOutputFormatsMap == null) { return new int[0]; } - return mInputOutputFormatsMap.getOutputs(inputFormat); + + int[] outputs = mInputOutputFormatsMap.getOutputs(inputFormat); + if (mHeicOutputFormats.size() > 0) { + // All reprocessing formats map contain JPEG. + int[] outputsWithHeic = Arrays.copyOf(outputs, outputs.length+1); + outputsWithHeic[outputs.length] = ImageFormat.HEIC; + return outputsWithHeic; + } else { + return outputs; + } } /** @@ -366,6 +445,8 @@ public final class StreamConfigurationMap { return mDepthOutputFormats.indexOfKey(internalFormat) >= 0; } else if (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) { return mDynamicDepthOutputFormats.indexOfKey(internalFormat) >= 0; + } else if (dataspace == HAL_DATASPACE_HEIF) { + return mHeicOutputFormats.indexOfKey(internalFormat) >= 0; } else { return getFormatsMap(/*output*/true).indexOfKey(internalFormat) >= 0; } @@ -479,6 +560,7 @@ public final class StreamConfigurationMap { StreamConfiguration[] configs = surfaceDataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations : surfaceDataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations : + surfaceDataspace == HAL_DATASPACE_HEIF ? mHeicConfigurations : mConfigurations; for (StreamConfiguration config : configs) { if (config.getFormat() == surfaceFormat && config.isOutput()) { @@ -512,9 +594,10 @@ public final class StreamConfigurationMap { int dataspace = imageFormatToDataspace(format); StreamConfiguration[] configs = - dataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations : - dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations : - mConfigurations; + dataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations : + dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations : + dataspace == HAL_DATASPACE_HEIF ? mHeicConfigurations : + mConfigurations; for (StreamConfiguration config : configs) { if ((config.getFormat() == internalFormat) && config.isOutput() && config.getSize().equals(size)) { @@ -1033,6 +1116,9 @@ public final class StreamConfigurationMap { Arrays.equals(mDynamicDepthMinFrameDurations, other.mDynamicDepthMinFrameDurations) && Arrays.equals(mDynamicDepthStallDurations, other.mDynamicDepthStallDurations) && + Arrays.equals(mHeicConfigurations, other.mHeicConfigurations) && + Arrays.equals(mHeicMinFrameDurations, other.mHeicMinFrameDurations) && + Arrays.equals(mHeicStallDurations, other.mHeicStallDurations) && Arrays.equals(mHighSpeedVideoConfigurations, other.mHighSpeedVideoConfigurations); } @@ -1049,7 +1135,9 @@ public final class StreamConfigurationMap { mConfigurations, mMinFrameDurations, mStallDurations, mDepthConfigurations, mDepthMinFrameDurations, mDepthStallDurations, mDynamicDepthConfigurations, mDynamicDepthMinFrameDurations, - mDynamicDepthStallDurations, mHighSpeedVideoConfigurations); + mDynamicDepthStallDurations, mHeicConfigurations, + mHeicMinFrameDurations, mHeicStallDurations, + mHighSpeedVideoConfigurations); } // Check that the argument is supported by #getOutputFormats or #getInputFormats @@ -1068,6 +1156,10 @@ public final class StreamConfigurationMap { if (mDynamicDepthOutputFormats.indexOfKey(internalFormat) >= 0) { return format; } + } else if (internalDataspace == HAL_DATASPACE_HEIF) { + if (mHeicOutputFormats.indexOfKey(internalFormat) >= 0) { + return format; + } } else { if (mAllOutputFormats.indexOfKey(internalFormat) >= 0) { return format; @@ -1108,8 +1200,9 @@ public final class StreamConfigurationMap { case HAL_PIXEL_FORMAT_Y16: return format; case ImageFormat.JPEG: + case ImageFormat.HEIC: throw new IllegalArgumentException( - "ImageFormat.JPEG is an unknown internal format"); + "An unknown internal format: " + format); default: return checkArgumentFormat(format); } @@ -1267,6 +1360,8 @@ public final class StreamConfigurationMap { * <ul> * <li>ImageFormat.JPEG => HAL_PIXEL_FORMAT_BLOB * <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_PIXEL_FORMAT_BLOB + * <li>ImageFormat.DEPTH_JPEG => HAL_PIXEL_FORMAT_BLOB + * <li>ImageFormat.HEIC => HAL_PIXEL_FORMAT_BLOB * <li>ImageFormat.DEPTH16 => HAL_PIXEL_FORMAT_Y16 * </ul> * </p> @@ -1292,6 +1387,7 @@ public final class StreamConfigurationMap { case ImageFormat.JPEG: case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.DEPTH_JPEG: + case ImageFormat.HEIC: return HAL_PIXEL_FORMAT_BLOB; case ImageFormat.DEPTH16: return HAL_PIXEL_FORMAT_Y16; @@ -1312,6 +1408,7 @@ public final class StreamConfigurationMap { * <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_DATASPACE_DEPTH * <li>ImageFormat.DEPTH16 => HAL_DATASPACE_DEPTH * <li>ImageFormat.DEPTH_JPEG => HAL_DATASPACE_DYNAMIC_DEPTH + * <li>ImageFormat.HEIC => HAL_DATASPACE_HEIF * <li>others => HAL_DATASPACE_UNKNOWN * </ul> * </p> @@ -1343,6 +1440,8 @@ public final class StreamConfigurationMap { return HAL_DATASPACE_DEPTH; case ImageFormat.DEPTH_JPEG: return HAL_DATASPACE_DYNAMIC_DEPTH; + case ImageFormat.HEIC: + return HAL_DATASPACE_HEIF; default: return HAL_DATASPACE_UNKNOWN; } @@ -1394,14 +1493,17 @@ public final class StreamConfigurationMap { !output ? mInputFormats : dataspace == HAL_DATASPACE_DEPTH ? mDepthOutputFormats : dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthOutputFormats : + dataspace == HAL_DATASPACE_HEIF ? mHeicOutputFormats : highRes ? mHighResOutputFormats : mOutputFormats; int sizesCount = formatsMap.get(format); if ( ((!output || (dataspace == HAL_DATASPACE_DEPTH || - dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) && sizesCount == 0) || + dataspace == HAL_DATASPACE_DYNAMIC_DEPTH || + dataspace == HAL_DATASPACE_HEIF)) && sizesCount == 0) || (output && (dataspace != HAL_DATASPACE_DEPTH && - dataspace != HAL_DATASPACE_DYNAMIC_DEPTH) && + dataspace != HAL_DATASPACE_DYNAMIC_DEPTH && + dataspace != HAL_DATASPACE_HEIF) && mAllOutputFormats.get(format) == 0)) { // Only throw if this is really not supported at all throw new IllegalArgumentException("format not available"); @@ -1413,10 +1515,12 @@ public final class StreamConfigurationMap { StreamConfiguration[] configurations = (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations : + (dataspace == HAL_DATASPACE_HEIF) ? mHeicConfigurations : mConfigurations; StreamConfigurationDuration[] minFrameDurations = (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations : (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthMinFrameDurations : + (dataspace == HAL_DATASPACE_HEIF) ? mHeicMinFrameDurations : mMinFrameDurations; for (StreamConfiguration config : configurations) { @@ -1445,7 +1549,8 @@ public final class StreamConfigurationMap { } // Dynamic depth streams can have both fast and also high res modes. - if ((sizeIndex != sizesCount) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) { + if ((sizeIndex != sizesCount) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH || + dataspace == HAL_DATASPACE_HEIF)) { if (sizeIndex > sizesCount) { throw new AssertionError( @@ -1485,6 +1590,9 @@ public final class StreamConfigurationMap { // Only one publicly dynamic depth format is available. formats[i++] = ImageFormat.DEPTH_JPEG; } + if (mHeicOutputFormats.size() > 0) { + formats[i++] = ImageFormat.HEIC; + } } if (formats.length != i) { throw new AssertionError("Too few formats " + i + ", expected " + formats.length); @@ -1529,10 +1637,14 @@ public final class StreamConfigurationMap { case DURATION_MIN_FRAME: return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations : (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? - mDynamicDepthMinFrameDurations : mMinFrameDurations; + mDynamicDepthMinFrameDurations : + (dataspace == HAL_DATASPACE_HEIF) ? mHeicMinFrameDurations : + mMinFrameDurations; + case DURATION_STALL: return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthStallDurations : (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthStallDurations : + (dataspace == HAL_DATASPACE_HEIF) ? mHeicStallDurations : mStallDurations; default: throw new IllegalArgumentException("duration was invalid"); @@ -1546,6 +1658,7 @@ public final class StreamConfigurationMap { if (output) { size += mDepthOutputFormats.size(); size += mDynamicDepthOutputFormats.size(); + size += mHeicOutputFormats.size(); } return size; @@ -1569,6 +1682,7 @@ public final class StreamConfigurationMap { StreamConfiguration[] configurations = (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations : + (dataspace == HAL_DATASPACE_HEIF) ? mHeicConfigurations : mConfigurations; for (int i = 0; i < configurations.length; i++) { @@ -1767,6 +1881,8 @@ public final class StreamConfigurationMap { return "RAW_DEPTH"; case ImageFormat.PRIVATE: return "PRIVATE"; + case ImageFormat.HEIC: + return "HEIC"; default: return "UNKNOWN"; } @@ -1795,7 +1911,7 @@ public final class StreamConfigurationMap { private static final int HAL_DATASPACE_DEPTH = 0x1000; private static final int HAL_DATASPACE_DYNAMIC_DEPTH = 0x1002; - + private static final int HAL_DATASPACE_HEIF = 0x1003; private static final long DURATION_20FPS_NS = 50000000L; /** * @see #getDurations(int, int) @@ -1815,6 +1931,10 @@ public final class StreamConfigurationMap { private final StreamConfigurationDuration[] mDynamicDepthMinFrameDurations; private final StreamConfigurationDuration[] mDynamicDepthStallDurations; + private final StreamConfiguration[] mHeicConfigurations; + private final StreamConfigurationDuration[] mHeicMinFrameDurations; + private final StreamConfigurationDuration[] mHeicStallDurations; + private final HighSpeedVideoConfiguration[] mHighSpeedVideoConfigurations; private final ReprocessFormatsMap mInputOutputFormatsMap; @@ -1834,6 +1954,9 @@ public final class StreamConfigurationMap { private final SparseIntArray mDepthOutputFormats = new SparseIntArray(); /** internal format -> num dynamic depth output sizes mapping, for HAL_DATASPACE_DYNAMIC_DEPTH */ private final SparseIntArray mDynamicDepthOutputFormats = new SparseIntArray(); + /** internal format -> num heic output sizes mapping, for HAL_DATASPACE_HEIF */ + private final SparseIntArray mHeicOutputFormats = new SparseIntArray(); + /** High speed video Size -> FPS range count mapping*/ private final HashMap</*HighSpeedVideoSize*/Size, /*Count*/Integer> mHighSpeedVideoSizeMap = new HashMap<Size, Integer>(); diff --git a/core/java/android/hardware/display/NightDisplayListener.java b/core/java/android/hardware/display/NightDisplayListener.java new file mode 100644 index 000000000000..468f8332dc2b --- /dev/null +++ b/core/java/android/hardware/display/NightDisplayListener.java @@ -0,0 +1,153 @@ +/* + * 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.display; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings.Secure; + +import java.time.LocalTime; + +/** + * @hide + */ +public class NightDisplayListener { + + private final Context mContext; + private final int mUserId; + private final ColorDisplayManager mManager; + + private ContentObserver mContentObserver; + private Callback mCallback; + + public NightDisplayListener(@NonNull Context context) { + this(context, ActivityManager.getCurrentUser()); + } + + public NightDisplayListener(@NonNull Context context, @UserIdInt int userId) { + mContext = context.getApplicationContext(); + mUserId = userId; + mManager = mContext.getSystemService(ColorDisplayManager.class); + } + + /** + * Register a callback to be invoked whenever the Night display settings are changed. + */ + public void setCallback(Callback callback) { + final Callback oldCallback = mCallback; + if (oldCallback != callback) { + mCallback = callback; + + if (mContentObserver == null) { + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + onSettingChanged(uri); + } + }; + } + + if (callback == null) { + // Stop listening for changes now that there IS NOT a callback. + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + } else if (oldCallback == null) { + // Start listening for changes now that there IS a callback. + final ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE), + false /* notifyForDescendants */, mContentObserver, mUserId); + } + } + } + + private void onSettingChanged(Uri uri) { + final String setting = uri == null ? null : uri.getLastPathSegment(); + if (setting == null || mCallback == null) { + return; + } + + switch (setting) { + case Secure.NIGHT_DISPLAY_ACTIVATED: + mCallback.onActivated(mManager.isNightDisplayActivated()); + break; + case Secure.NIGHT_DISPLAY_AUTO_MODE: + mCallback.onAutoModeChanged(mManager.getNightDisplayAutoMode()); + break; + case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME: + mCallback.onCustomStartTimeChanged(mManager.getNightDisplayCustomStartTime()); + break; + case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME: + mCallback.onCustomEndTimeChanged(mManager.getNightDisplayCustomEndTime()); + break; + case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE: + mCallback.onColorTemperatureChanged(mManager.getNightDisplayColorTemperature()); + break; + } + } + + /** + * Callback invoked whenever the Night display settings are changed. + */ + public interface Callback { + /** + * Callback invoked when the activated state changes. + * + * @param activated {@code true} if Night display is activated + */ + default void onActivated(boolean activated) {} + /** + * Callback invoked when the auto mode changes. + * + * @param autoMode the auto mode to use + */ + default void onAutoModeChanged(int autoMode) {} + /** + * Callback invoked when the time to automatically activate Night display changes. + * + * @param startTime the local time to automatically activate Night display + */ + default void onCustomStartTimeChanged(LocalTime startTime) {} + /** + * Callback invoked when the time to automatically deactivate Night display changes. + * + * @param endTime the local time to automatically deactivate Night display + */ + default void onCustomEndTimeChanged(LocalTime endTime) {} + + /** + * Callback invoked when the color temperature changes. + * + * @param colorTemperature the color temperature to tint the screen + */ + default void onColorTemperatureChanged(int colorTemperature) {} + } +} diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index c9a7830d50f5..efe24e50272a 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -565,16 +565,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ public static String getErrorString(Context context, int errMsg, int vendorCode) { switch (errMsg) { - case FACE_ERROR_UNABLE_TO_PROCESS: - return context.getString( - com.android.internal.R.string.face_error_unable_to_process); case FACE_ERROR_HW_UNAVAILABLE: return context.getString( com.android.internal.R.string.face_error_hw_not_available); - case FACE_ERROR_NO_SPACE: - return context.getString(com.android.internal.R.string.face_error_no_space); + case FACE_ERROR_UNABLE_TO_PROCESS: + return context.getString( + com.android.internal.R.string.face_error_unable_to_process); case FACE_ERROR_TIMEOUT: return context.getString(com.android.internal.R.string.face_error_timeout); + case FACE_ERROR_NO_SPACE: + return context.getString(com.android.internal.R.string.face_error_no_space); case FACE_ERROR_CANCELED: return context.getString(com.android.internal.R.string.face_error_canceled); case FACE_ERROR_LOCKOUT: @@ -629,6 +629,24 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return context.getString(R.string.face_acquired_poor_gaze); case FACE_ACQUIRED_NOT_DETECTED: return context.getString(R.string.face_acquired_not_detected); + case FACE_ACQUIRED_TOO_MUCH_MOTION: + return context.getString(R.string.face_acquired_too_much_motion); + case FACE_ACQUIRED_RECALIBRATE: + return context.getString(R.string.face_acquired_recalibrate); + case FACE_ACQUIRED_TOO_DIFFERENT: + return context.getString(R.string.face_acquired_too_different); + case FACE_ACQUIRED_TOO_SIMILAR: + return context.getString(R.string.face_acquired_too_similar); + case FACE_ACQUIRED_PAN_TOO_EXTREME: + return context.getString(R.string.face_acquired_pan_too_extreme); + case FACE_ACQUIRED_TILT_TOO_EXTREME: + return context.getString(R.string.face_acquired_tilt_too_extreme); + case FACE_ACQUIRED_ROLL_TOO_EXTREME: + return context.getString(R.string.face_acquired_roll_too_extreme); + case FACE_ACQUIRED_FACE_OBSCURED: + return context.getString(R.string.face_acquired_obscured); + case FACE_ACQUIRED_START: + return null; case FACE_ACQUIRED_VENDOR: { String[] msgArray = context.getResources().getStringArray( R.array.face_acquired_vendor); diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index bb9821169a54..80d404d03c75 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -949,17 +949,17 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ public static String getErrorString(Context context, int errMsg, int vendorCode) { switch (errMsg) { + case FINGERPRINT_ERROR_HW_UNAVAILABLE: + return context.getString( + com.android.internal.R.string.fingerprint_error_hw_not_available); case FINGERPRINT_ERROR_UNABLE_TO_PROCESS: return context.getString( com.android.internal.R.string.fingerprint_error_unable_to_process); - case FINGERPRINT_ERROR_HW_UNAVAILABLE: - return context.getString( - com.android.internal.R.string.fingerprint_error_hw_not_available); + case FINGERPRINT_ERROR_TIMEOUT: + return context.getString(com.android.internal.R.string.fingerprint_error_timeout); case FINGERPRINT_ERROR_NO_SPACE: return context.getString( com.android.internal.R.string.fingerprint_error_no_space); - case FINGERPRINT_ERROR_TIMEOUT: - return context.getString(com.android.internal.R.string.fingerprint_error_timeout); case FINGERPRINT_ERROR_CANCELED: return context.getString(com.android.internal.R.string.fingerprint_error_canceled); case FINGERPRINT_ERROR_LOCKOUT: diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 866ea16aa59d..2aca55aacf7a 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -68,6 +68,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -1889,7 +1890,8 @@ public class ConnectivityManager { * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive * changes. Must be extended by applications that use this API. * - * @return A {@link SocketKeepalive} object, which can be used to control this keepalive object. + * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the + * given socket. **/ public SocketKeepalive createSocketKeepalive(@NonNull Network network, @NonNull UdpEncapsulationSocket socket, @@ -1918,6 +1920,8 @@ public class ConnectivityManager { * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive * changes. Must be extended by applications that use this API. * + * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the + * given socket. * @hide */ @SystemApi @@ -1933,6 +1937,34 @@ public class ConnectivityManager { } /** + * Request that keepalives be started on a TCP socket. + * The socket must be established. + * + * @param network The {@link Network} the socket is on. + * @param socket The socket that needs to be kept alive. + * @param executor The executor on which callback will be invoked. This implementation assumes + * the provided {@link Executor} runs the callbacks in sequence with no + * concurrency. Failing this, no guarantee of correctness can be made. It is + * the responsibility of the caller to ensure the executor provides this + * guarantee. A simple way of creating such an executor is with the standard + * tool {@code Executors.newSingleThreadExecutor}. + * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive + * changes. Must be extended by applications that use this API. + * + * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the + * given socket. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) + public SocketKeepalive createSocketKeepalive(@NonNull Network network, + @NonNull Socket socket, + @NonNull Executor executor, + @NonNull Callback callback) { + return new TcpSocketKeepalive(mService, network, socket, executor, callback); + } + + /** * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. An attempt to add a route that * already exists is ignored, but treated as successful. @@ -3891,6 +3923,25 @@ public class ConnectivityManager { } /** + * Requests that the system open the captive portal app with the specified extras. + * + * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the + * corresponding permission. + * @param appExtras Extras to include in the app start intent. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + public void startCaptivePortalApp(Bundle appExtras) { + try { + mService.startCaptivePortalAppInternal(appExtras); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Determine whether the device is configured to avoid bad wifi. * @hide */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 1148ac1bc707..872671fa5697 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -27,6 +27,7 @@ import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.ProxyInfo; +import android.os.Bundle; import android.os.IBinder; import android.os.Messenger; import android.os.ParcelFileDescriptor; @@ -167,6 +168,7 @@ interface IConnectivityManager void setAcceptUnvalidated(in Network network, boolean accept, boolean always); void setAvoidUnvalidated(in Network network); void startCaptivePortalApp(in Network network); + void startCaptivePortalAppInternal(in Bundle appExtras); boolean getAvoidBadWifi(); int getMultipathPreference(in Network Network); @@ -188,6 +190,9 @@ interface IConnectivityManager int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr, String dstAddr); + void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds, + in Messenger messenger, in IBinder binder); + void stopKeepalive(in Network network, int slot); String getCaptivePortalServerUrl(); diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index c37837837a9e..273f8cd4f21d 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -178,6 +178,26 @@ public abstract class NetworkAgent extends Handler { */ public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13; + // TODO: move the above 2 constants down so they are in order once merge conflicts are resolved + /** + * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter. + * + * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the + * remote site will send ACK packets in response to the keepalive packets, the firmware also + * needs to be configured to properly filter the ACKs to prevent the system from waking up. + * This does not happen with UDP, so this message is TCP-specific. + * arg1 = slot number of the keepalive to filter for. + * obj = the keepalive packet to send repeatedly. + */ + public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16; + + /** + * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See + * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}. + * arg1 = slot number of the keepalive packet filter to remove. + */ + public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17; + /** * Sent by ConnectivityService to inform this network transport of signal strength thresholds * that when crossed should trigger a system wakeup and a NetworkCapabilities update. @@ -329,6 +349,14 @@ public abstract class NetworkAgent extends Handler { preventAutomaticReconnect(); break; } + case CMD_ADD_KEEPALIVE_PACKET_FILTER: { + addKeepalivePacketFilter(msg); + break; + } + case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: { + removeKeepalivePacketFilter(msg); + break; + } } } @@ -478,6 +506,24 @@ public abstract class NetworkAgent extends Handler { } /** + * Called by ConnectivityService to add specific packet filter to network hardware to block + * ACKs matching the sent keepalive packets. Implementations that support this feature must + * override this method. + */ + protected void addKeepalivePacketFilter(Message msg) { + onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + } + + /** + * Called by ConnectivityService to remove a packet filter installed with + * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature + * must override this method. + */ + protected void removeKeepalivePacketFilter(Message msg) { + onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + } + + /** * Called by ConnectivityService to inform this network transport of signal strength thresholds * that when crossed should trigger a system wakeup and a NetworkCapabilities update. */ diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java index 7ea1bef83669..07728beb9c64 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/core/java/android/net/SocketKeepalive.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -155,7 +156,7 @@ public abstract class SocketKeepalive implements AutoCloseable { @NonNull private final SocketKeepalive.Callback mCallback; @NonNull private final Looper mLooper; @NonNull final Messenger mMessenger; - @NonNull Integer mSlot; + @Nullable Integer mSlot; SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network, @NonNull Executor executor, @NonNull Callback callback) { diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java new file mode 100644 index 000000000000..8f6ee7bf2950 --- /dev/null +++ b/core/java/android/net/TcpSocketKeepalive.java @@ -0,0 +1,78 @@ +/* + * 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 android.annotation.NonNull; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; + +import java.io.FileDescriptor; +import java.net.Socket; +import java.util.concurrent.Executor; + +/** @hide */ +final class TcpSocketKeepalive extends SocketKeepalive { + + private final Socket mSocket; + + TcpSocketKeepalive(@NonNull IConnectivityManager service, + @NonNull Network network, + @NonNull Socket socket, + @NonNull Executor executor, + @NonNull Callback callback) { + super(service, network, executor, callback); + mSocket = socket; + } + + /** + * Starts keepalives. {@code mSocket} must be a connected TCP socket. + * + * - The application must not write to or read from the socket after calling this method, until + * onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail + * with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket + * experienced an error (as in poll(2) returned POLLERR); if this happens, the data received + * from the socket may be invalid, and the socket can't be recovered. + * - If the socket has data in the send or receive buffer, then this call will fail with + * {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed. + * An app could ensure this by using an application-layer protocol where it can receive + * acknowledgement that it will go into keepalive mode. It could then go into keepalive + * mode after having read the acknowledgement, draining the socket. + */ + @Override + void startImpl(int intervalSec) { + try { + final FileDescriptor fd = mSocket.getFileDescriptor$(); + mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder()); + } catch (RemoteException e) { + Log.e(TAG, "Error starting packet keepalive: ", e); + stopLooper(); + } + } + + @Override + void stopImpl() { + try { + if (mSlot != null) { + mService.stopKeepalive(mNetwork, mSlot); + } + } catch (RemoteException e) { + Log.e(TAG, "Error stopping packet keepalive: ", e); + stopLooper(); + } + } +} diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl index 7769ec2b65ac..a4a80e1efe6f 100644 --- a/core/java/android/net/ip/IIpClient.aidl +++ b/core/java/android/net/ip/IIpClient.aidl @@ -17,6 +17,7 @@ package android.net.ip; import android.net.ProxyInfoParcelable; import android.net.ProvisioningConfigurationParcelable; +import android.net.TcpKeepalivePacketDataParcelable; /** @hide */ oneway interface IIpClient { @@ -29,4 +30,6 @@ oneway interface IIpClient { void setTcpBufferSizes(in String tcpBufferSizes); void setHttpProxy(in ProxyInfoParcelable proxyInfo); void setMulticastFilter(boolean enabled); + void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt); + void removeKeepalivePacketFilter(int slot); } diff --git a/core/java/android/os/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java index b6e2b69bbe3c..a107a7a2cfb8 100644 --- a/core/java/android/os/BatterySaverPolicyConfig.java +++ b/core/java/android/os/BatterySaverPolicyConfig.java @@ -47,10 +47,11 @@ public final class BatterySaverPolicyConfig implements Parcelable { private final boolean mEnableAdjustBrightness; private final boolean mEnableDataSaver; private final boolean mEnableFirewall; + private final boolean mEnableNightMode; private final boolean mEnableQuickDoze; private final boolean mForceAllAppsStandby; private final boolean mForceBackgroundCheck; - private final int mGpsMode; + private final int mLocationMode; private BatterySaverPolicyConfig(Builder in) { mAdjustBrightnessFactor = Math.max(0, Math.min(in.mAdjustBrightnessFactor, 1f)); @@ -67,11 +68,12 @@ public final class BatterySaverPolicyConfig implements Parcelable { mEnableAdjustBrightness = in.mEnableAdjustBrightness; mEnableDataSaver = in.mEnableDataSaver; mEnableFirewall = in.mEnableFirewall; + mEnableNightMode = in.mEnableNightMode; mEnableQuickDoze = in.mEnableQuickDoze; mForceAllAppsStandby = in.mForceAllAppsStandby; mForceBackgroundCheck = in.mForceBackgroundCheck; - mGpsMode = Math.max(PowerManager.MIN_LOCATION_MODE, - Math.min(in.mGpsMode, PowerManager.MAX_LOCATION_MODE)); + mLocationMode = Math.max(PowerManager.MIN_LOCATION_MODE, + Math.min(in.mLocationMode, PowerManager.MAX_LOCATION_MODE)); } private BatterySaverPolicyConfig(Parcel in) { @@ -101,10 +103,11 @@ public final class BatterySaverPolicyConfig implements Parcelable { mEnableAdjustBrightness = in.readBoolean(); mEnableDataSaver = in.readBoolean(); mEnableFirewall = in.readBoolean(); + mEnableNightMode = in.readBoolean(); mEnableQuickDoze = in.readBoolean(); mForceAllAppsStandby = in.readBoolean(); mForceBackgroundCheck = in.readBoolean(); - mGpsMode = Math.max(PowerManager.MIN_LOCATION_MODE, + mLocationMode = Math.max(PowerManager.MIN_LOCATION_MODE, Math.min(in.readInt(), PowerManager.MAX_LOCATION_MODE)); } @@ -150,10 +153,11 @@ public final class BatterySaverPolicyConfig implements Parcelable { dest.writeBoolean(mEnableAdjustBrightness); dest.writeBoolean(mEnableDataSaver); dest.writeBoolean(mEnableFirewall); + dest.writeBoolean(mEnableNightMode); dest.writeBoolean(mEnableQuickDoze); dest.writeBoolean(mForceAllAppsStandby); dest.writeBoolean(mForceBackgroundCheck); - dest.writeInt(mGpsMode); + dest.writeInt(mLocationMode); } @Override @@ -168,11 +172,12 @@ public final class BatterySaverPolicyConfig implements Parcelable { + "animation_disabled=" + mDisableAnimation + "," + "aod_disabled=" + mDisableAod + "," + "datasaver_disabled=" + !mEnableDataSaver + "," + + "enable_night_mode=" + mEnableNightMode + "," + "firewall_disabled=" + !mEnableFirewall + "," + "force_all_apps_standby=" + mForceAllAppsStandby + "," + "force_background_check=" + mForceBackgroundCheck + "," + "fullbackup_deferred=" + mDeferFullBackup + "," - + "gps_mode=" + mGpsMode + "," + + "gps_mode=" + mLocationMode + "," + "keyvaluebackup_deferred=" + mDeferKeyValueBackup + "," + "launch_boost_disabled=" + mDisableLaunchBoost + "," + "optional_sensors_disabled=" + mDisableOptionalSensors + "," @@ -260,6 +265,11 @@ public final class BatterySaverPolicyConfig implements Parcelable { return mEnableFirewall; } + /** Whether or not to enable night mode while in Battery Saver. */ + public boolean getEnableNightMode() { + return mEnableNightMode; + } + /** Whether or not to enable Quick Doze while in Battery Saver. */ public boolean getEnableQuickDoze() { return mEnableQuickDoze; @@ -275,9 +285,9 @@ public final class BatterySaverPolicyConfig implements Parcelable { return mForceBackgroundCheck; } - /** The GPS mode while in Battery Saver. */ - public int getGpsMode() { - return mGpsMode; + /** The location mode while in Battery Saver. */ + public int getLocationMode() { + return mLocationMode; } /** Builder class for constructing {@link BatterySaverPolicyConfig} objects. */ @@ -297,10 +307,11 @@ public final class BatterySaverPolicyConfig implements Parcelable { private boolean mEnableAdjustBrightness = false; private boolean mEnableDataSaver = false; private boolean mEnableFirewall = false; + private boolean mEnableNightMode = false; private boolean mEnableQuickDoze = false; private boolean mForceAllAppsStandby = false; private boolean mForceBackgroundCheck = false; - private int mGpsMode = PowerManager.LOCATION_MODE_NO_CHANGE; + private int mLocationMode = PowerManager.LOCATION_MODE_NO_CHANGE; public Builder() { } @@ -424,6 +435,13 @@ public final class BatterySaverPolicyConfig implements Parcelable { return this; } + /** Set whether or not to enable night mode while in Battery Saver. */ + @NonNull + public Builder setEnableNightMode(boolean enableNightMode) { + mEnableNightMode = enableNightMode; + return this; + } + /** Set whether or not to enable Quick Doze while in Battery Saver. */ @NonNull public Builder setEnableQuickDoze(boolean enableQuickDoze) { @@ -445,10 +463,10 @@ public final class BatterySaverPolicyConfig implements Parcelable { return this; } - /** Set the GPS mode while in Battery Saver. */ + /** Set the location mode while in Battery Saver. */ @NonNull - public Builder setGpsMode(@PowerManager.LocationPowerSaveMode int gpsMode) { - mGpsMode = gpsMode; + public Builder setLocationMode(@PowerManager.LocationPowerSaveMode int locationMode) { + mLocationMode = locationMode; return this; } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 2d61a4ee95d9..83a7654d494b 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1120,11 +1120,9 @@ public class Build { /** The name identifying the system partition. */ public static final String PARTITION_NAME_SYSTEM = "system"; - private String mName; - private String mFingerprint; - private long mTimeMs; - - public Partition() {} + private final String mName; + private final String mFingerprint; + private final long mTimeMs; private Partition(String name, String fingerprint, long timeMs) { mName = name; diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 68f9288a93cf..269c781397ad 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -19,6 +19,7 @@ package android.os; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.AssetFileDescriptor; @@ -59,6 +60,9 @@ public class GraphicsEnvironment { private static final boolean DEBUG = false; private static final String TAG = "GraphicsEnvironment"; + private static final String SYSTEM_DRIVER_NAME = "system"; + private static final String SYSTEM_DRIVER_VERSION_NAME = ""; + private static final long SYSTEM_DRIVER_VERSION_CODE = 0; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; @@ -74,9 +78,14 @@ public class GraphicsEnvironment { * Set up GraphicsEnvironment */ public void setup(Context context, Bundle coreSettings) { - setupGpuLayers(context, coreSettings); - setupAngle(context, coreSettings, context.getPackageName()); - chooseDriver(context, coreSettings); + final PackageManager pm = context.getPackageManager(); + final String packageName = context.getPackageName(); + setupGpuLayers(context, coreSettings, pm, packageName); + setupAngle(context, coreSettings, pm, packageName); + if (!chooseDriver(context, coreSettings, pm, packageName)) { + setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, + packageName); + } } /** @@ -102,11 +111,10 @@ public class GraphicsEnvironment { /** * Return the debug layer app's on-disk and in-APK lib directories */ - private static String getDebugLayerAppPaths(Context context, String app) { + private static String getDebugLayerAppPaths(PackageManager pm, String app) { final ApplicationInfo appInfo; try { - appInfo = context.getPackageManager().getApplicationInfo( - app, PackageManager.MATCH_ALL); + appInfo = pm.getApplicationInfo(app, PackageManager.MATCH_ALL); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Debug layer app '" + app + "' not installed"); @@ -132,8 +140,8 @@ public class GraphicsEnvironment { * Set up layer search paths for all apps * If debuggable, check for additional debug settings */ - private void setupGpuLayers(Context context, Bundle coreSettings) { - + private void setupGpuLayers( + Context context, Bundle coreSettings, PackageManager pm, String packageName) { String layerPaths = ""; // Only enable additional debug functionality if the following conditions are met: @@ -149,8 +157,6 @@ public class GraphicsEnvironment { final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP); - final String packageName = context.getPackageName(); - if ((gpuDebugApp != null && packageName != null) && (!gpuDebugApp.isEmpty() && !packageName.isEmpty()) && gpuDebugApp.equals(packageName)) { @@ -161,14 +167,13 @@ public class GraphicsEnvironment { // the layers specified by the app. layerPaths = mDebugLayerPath + ":"; - // If there is a debug layer app specified, add its path. final String gpuDebugLayerApp = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP); if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) { Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp); - final String paths = getDebugLayerAppPaths(context, 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 @@ -280,11 +285,11 @@ public class GraphicsEnvironment { /** * Get the ANGLE package name. */ - private String getAnglePackageName(Context context) { + private String getAnglePackageName(PackageManager pm) { final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID); - final List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities( - intent, PackageManager.MATCH_SYSTEM_ONLY); + final List<ResolveInfo> resolveInfos = + pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY); if (resolveInfos.size() != 1) { Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: " + resolveInfos.size()); @@ -369,14 +374,13 @@ public class GraphicsEnvironment { */ private boolean setupAngleRulesApk(String anglePkgName, ApplicationInfo angleInfo, - Context context, + PackageManager pm, String packageName, String paths, String devOptIn) { // Pass the rules file to loader for ANGLE decisions try { - final AssetManager angleAssets = - context.getPackageManager().getResourcesForApplication(angleInfo).getAssets(); + final AssetManager angleAssets = pm.getResourcesForApplication(angleInfo).getAssets(); try { final AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE); @@ -411,7 +415,7 @@ public class GraphicsEnvironment { /** * Pass ANGLE details down to trigger enable logic */ - public void setupAngle(Context context, Bundle bundle, String packageName) { + public void setupAngle(Context context, Bundle bundle, PackageManager pm, String packageName) { if (packageName.isEmpty()) { Log.v(TAG, "No package name available yet, skipping ANGLE setup"); return; @@ -449,7 +453,7 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn); } - final String anglePkgName = getAnglePackageName(context); + final String anglePkgName = getAnglePackageName(pm); if (anglePkgName.isEmpty()) { Log.e(TAG, "Failed to find ANGLE package."); return; @@ -457,8 +461,7 @@ public class GraphicsEnvironment { final ApplicationInfo angleInfo; try { - angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName, - PackageManager.MATCH_SYSTEM_ONLY); + angleInfo = pm.getApplicationInfo(anglePkgName, PackageManager.MATCH_SYSTEM_ONLY); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed"); return; @@ -480,7 +483,7 @@ public class GraphicsEnvironment { return; } - if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) { + if (setupAngleRulesApk(anglePkgName, angleInfo, pm, packageName, paths, devOptIn)) { // We setup ANGLE with rules from the APK, so we're done here. return; } @@ -489,28 +492,30 @@ public class GraphicsEnvironment { /** * Choose whether the current process should use the builtin or an updated driver. */ - private static void chooseDriver(Context context, Bundle coreSettings) { + private static boolean chooseDriver( + Context context, Bundle coreSettings, PackageManager pm, String packageName) { final String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); if (driverPackageName == null || driverPackageName.isEmpty()) { - return; + return false; } - final ApplicationInfo driverInfo; + final PackageInfo driverPackageInfo; try { - driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName, - PackageManager.MATCH_SYSTEM_ONLY); + driverPackageInfo = + pm.getPackageInfo(driverPackageName, PackageManager.MATCH_SYSTEM_ONLY); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "driver package '" + driverPackageName + "' not installed"); - return; + return false; } // O drivers are restricted to the sphal linker namespace, so don't try to use // packages unless they declare they're compatible with that restriction. - if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) { + final ApplicationInfo driverAppInfo = driverPackageInfo.applicationInfo; + if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) { if (DEBUG) { Log.w(TAG, "updated driver package is not known to be compatible with O"); } - return; + return false; } // To minimize risk of driver updates crippling the device beyond user repair, never use an @@ -519,7 +524,7 @@ public class GraphicsEnvironment { final ApplicationInfo ai = context.getApplicationInfo(); if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) { if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app"); - return; + return false; } // GAME_DRIVER_ALL_APPS @@ -531,28 +536,28 @@ public class GraphicsEnvironment { if (DEBUG) { Log.w(TAG, "Game Driver is turned off on this device"); } - return; + return false; } if (gameDriverAllApps != 1) { // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS) - .contains(ai.packageName)) { + .contains(packageName)) { if (DEBUG) { - Log.w(TAG, ai.packageName + " opts out from Game Driver."); + Log.w(TAG, packageName + " opts out from Game Driver."); } - return; + return false; } final boolean isOptIn = getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) - .contains(ai.packageName); + .contains(packageName); if (!isOptIn && !getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_WHITELIST) - .contains(ai.packageName)) { + .contains(packageName)) { if (DEBUG) { - Log.w(TAG, ai.packageName + " is not on the whitelist."); + Log.w(TAG, packageName + " is not on the whitelist."); } - return; + return false; } if (!isOptIn) { @@ -566,12 +571,12 @@ public class GraphicsEnvironment { final Blacklists blacklistsProto = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList(); - final long driverVersionCode = driverInfo.longVersionCode; + final long driverVersionCode = driverAppInfo.longVersionCode; for (Blacklist blacklist : blacklists) { if (blacklist.getVersionCode() == driverVersionCode) { - for (String packageName : blacklist.getPackageNamesList()) { - if (packageName == ai.packageName) { - return; + for (String pkgName : blacklist.getPackageNamesList()) { + if (pkgName == packageName) { + return false; } } break; @@ -586,27 +591,32 @@ public class GraphicsEnvironment { } } - final String abi = chooseAbi(driverInfo); + final String abi = chooseAbi(driverAppInfo); if (abi == null) { if (DEBUG) { // This is the normal case for the pre-installed empty driver package, don't spam - if (driverInfo.isUpdatedSystemApp()) { + if (driverAppInfo.isUpdatedSystemApp()) { Log.w(TAG, "updated driver package has no compatible native libraries"); } } - return; + return false; } + setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode, + packageName); + final StringBuilder sb = new StringBuilder(); - sb.append(driverInfo.nativeLibraryDir) + sb.append(driverAppInfo.nativeLibraryDir) .append(File.pathSeparator); - sb.append(driverInfo.sourceDir) + sb.append(driverAppInfo.sourceDir) .append("!/lib/") .append(abi); final String paths = sb.toString(); if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths); setDriverPath(paths); + + return true; } /** @@ -646,7 +656,8 @@ public class GraphicsEnvironment { private static native void setDebugLayers(String layers); private static native void setDebugLayersGLES(String layers); private static native void setDriverPath(String path); - private static native void setAngleInfo(String path, String appPackage, - String devOptIn, FileDescriptor rulesFd, - long rulesOffset, long rulesLength); + private static native void setGpuStats(String driverPackageName, String driverVersionName, + long driverVersionCode, String appPackageName); + private static native void setAngleInfo(String path, String appPackage, String devOptIn, + FileDescriptor rulesFd, long rulesOffset, long rulesLength); } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 093897a3e476..bdef57518ac9 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -42,7 +42,7 @@ interface IPowerManager boolean isWakeLockLevelSupported(int level); void userActivity(long time, int event, int flags); - void wakeUp(long time, String reason, String opPackageName); + void wakeUp(long time, int reason, String details, String opPackageName); void goToSleep(long time, int reason, int flags); void nap(long time); boolean isInteractive(); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index be673ad14848..2ecf9d1159f0 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -435,6 +435,106 @@ public final class PowerManager { public static final int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0; /** + * @hide + */ + @IntDef(prefix = { "WAKE_REASON_" }, value = { + WAKE_REASON_UNKNOWN, + WAKE_REASON_POWER_BUTTON, + WAKE_REASON_APPLICATION, + WAKE_REASON_PLUGGED_IN, + WAKE_REASON_GESTURE, + WAKE_REASON_CAMERA_LAUNCH, + WAKE_REASON_WAKE_KEY, + WAKE_REASON_WAKE_MOTION, + WAKE_REASON_HDMI, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WakeReason{} + + /** + * Wake up reason code: Waking for an unknown reason. + * @hide + */ + public static final int WAKE_REASON_UNKNOWN = 0; + + /** + * Wake up reason code: Waking up due to power button press. + * @hide + */ + public static final int WAKE_REASON_POWER_BUTTON = 1; + + /** + * Wake up reason code: Waking up because an application requested it. + * @hide + */ + public static final int WAKE_REASON_APPLICATION = 2; + + /** + * Wake up reason code: Waking up due to being plugged in or docked on a wireless charger. + * @hide + */ + public static final int WAKE_REASON_PLUGGED_IN = 3; + + /** + * Wake up reason code: Waking up due to a user performed gesture (e.g. douple tapping on the + * screen). + * @hide + */ + public static final int WAKE_REASON_GESTURE = 4; + + /** + * Wake up reason code: Waking up due to the camera being launched. + * @hide + */ + public static final int WAKE_REASON_CAMERA_LAUNCH = 5; + + /** + * Wake up reason code: Waking up because a wake key other than power was pressed. + * @hide + */ + public static final int WAKE_REASON_WAKE_KEY = 6; + + /** + * Wake up reason code: Waking up because a wake motion was performed. + * + * For example, a trackball that was set to wake the device up was spun. + * @hide + */ + public static final int WAKE_REASON_WAKE_MOTION = 7; + + /** + * Wake up reason code: Waking due to HDMI. + * @hide + */ + public static final int WAKE_REASON_HDMI = 8; + + /** + * Wake up reason code: Waking due to the lid being opened. + * @hide + */ + public static final int WAKE_REASON_LID = 9; + + /** + * Convert the wake reason to a string for debugging purposes. + * @hide + */ + public static String wakeReasonToString(@WakeReason int wakeReason) { + switch (wakeReason) { + case WAKE_REASON_UNKNOWN: return "WAKE_REASON_UNKNOWN"; + case WAKE_REASON_POWER_BUTTON: return "WAKE_REASON_POWER_BUTTON"; + case WAKE_REASON_APPLICATION: return "WAKE_REASON_APPLICATION"; + case WAKE_REASON_PLUGGED_IN: return "WAKE_REASON_PLUGGED_IN"; + case WAKE_REASON_GESTURE: return "WAKE_REASON_GESTURE"; + case WAKE_REASON_CAMERA_LAUNCH: return "WAKE_REASON_CAMERA_LAUNCH"; + case WAKE_REASON_WAKE_KEY: return "WAKE_REASON_WAKE_KEY"; + case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION"; + case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI"; + case WAKE_REASON_LID: return "WAKE_REASON_LID"; + default: return Integer.toString(wakeReason); + } + } + + /** * The value to pass as the 'reason' argument to reboot() to reboot into * recovery mode for tasks other than applying system updates, such as * doing factory resets. @@ -975,22 +1075,68 @@ public final class PowerManager { * @see #userActivity * @see #goToSleep * + * @deprecated Use {@link #wakeUp(long, int, String)} instead. * @removed Requires signature permission. */ + @Deprecated public void wakeUp(long time) { - try { - mService.wakeUp(time, "wakeUp", mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + wakeUp(time, WAKE_REASON_UNKNOWN, "wakeUp"); } /** + * Forces the device to wake up from sleep. + * <p> + * If the device is currently asleep, wakes it up, otherwise does nothing. + * This is what happens when the power key is pressed to turn on the screen. + * </p><p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param time The time when the request to wake up was issued, in the + * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly + * order the wake up request with other power management functions. It should be set + * to the timestamp of the input event that caused the request to wake up. + * + * @param details A free form string to explain the specific details behind the wake up for + * debugging purposes. + * + * @see #userActivity + * @see #goToSleep + * + * @deprecated Use {@link #wakeUp(long, int, String)} instead. + * @hide + */ + @Deprecated + public void wakeUp(long time, String details) { + wakeUp(time, WAKE_REASON_UNKNOWN, details); + } + + /** + * Forces the device to wake up from sleep. + * <p> + * If the device is currently asleep, wakes it up, otherwise does nothing. + * This is what happens when the power key is pressed to turn on the screen. + * </p><p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param time The time when the request to wake up was issued, in the + * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly + * order the wake up request with other power management functions. It should be set + * to the timestamp of the input event that caused the request to wake up. + * + * @param reason The reason for the wake up. + * + * @param details A free form string to explain the specific details behind the wake up for + * debugging purposes. + * + * @see #userActivity + * @see #goToSleep * @hide */ - public void wakeUp(long time, String reason) { + public void wakeUp(long time, @WakeReason int reason, String details) { try { - mService.wakeUp(time, reason, mContext.getOpPackageName()); + mService.wakeUp(time, reason, details, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index d2ab053eb4e6..9e97e375753c 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -526,11 +526,12 @@ public class Process { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, + @Nullable String sandboxId, @Nullable String[] zygoteArgs) { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, - packagesForUid, visibleVols, /*useBlastulaPool=*/ true, zygoteArgs); + packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs); } /** @hide */ @@ -547,11 +548,12 @@ public class Process { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, + @Nullable String sandboxId, @Nullable String[] zygoteArgs) { return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, - packagesForUid, visibleVols, /*useBlastulaPool=*/ false, zygoteArgs); + packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs); } /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 0a60764428dc..e2b5730e10f4 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -734,10 +734,13 @@ public class UserManager { public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; /** - * Specifies if what is copied in the clipboard of this profile can - * be pasted in related profiles. Does not restrict if the clipboard of related profiles can be - * pasted in this profile. - * The default value is <code>false</code>. + * Specifies if the clipboard contents can be exported by pasting the data into other users or + * profiles. This restriction doesn't prevent import, such as someone pasting clipboard data + * from other profiles or users. The default value is {@code false}. + * + * <p><strong>Note</strong>: Because it's possible to extract data from screenshots using + * optical character recognition (OCR), we strongly recommend combining this user restriction + * with {@link DevicePolicyManager#setScreenCaptureDisabled(ComponentName, boolean)}. * * <p>Key for user restrictions. * <p>Type: Boolean diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index e94ad2b8989e..ee3d35427b29 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -324,13 +324,15 @@ public class ZygoteProcess { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, + @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false, - packageName, packagesForUid, visibleVols, useBlastulaPool, zygoteArgs); + packageName, packagesForUid, visibleVols, sandboxId, + useBlastulaPool, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -541,6 +543,7 @@ public class ZygoteProcess { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, + @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { @@ -639,6 +642,10 @@ public class ZygoteProcess { argsForZygote.add(sb.toString()); } + if (sandboxId != null) { + argsForZygote.add("--sandbox-id=" + sandboxId); + } + argsForZygote.add(processClass); if (extraArgs != null) { @@ -1014,7 +1021,7 @@ public class ZygoteProcess { gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, abi, instructionSet, null /* appDataDir */, null /* invokeWith */, true /* startChildZygote */, null /* packageName */, - null /* packagesForUid */, null /* visibleVolumes */, + null /* packagesForUid */, null /* visibleVolumes */, null /* sandboxId */, false /* useBlastulaPool */, extraArgs); } catch (ZygoteStartFailedEx ex) { throw new RuntimeException("Starting child-zygote through Zygote failed", ex); diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index f521c683896e..03b2c2c4c6f2 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -132,4 +132,9 @@ public abstract class StorageManagerInternal { * @param listener The listener that will be notified on reset events. */ public abstract void addResetListener(ResetListener listener); + + /** + * Return the sandboxId for the given package on external storage. + */ + public abstract String getSandboxId(String packageName); } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index de54a8aa5548..f63c0adbdf4b 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -772,19 +772,6 @@ public class CallLog { * @param callBlockReason The reason why the call is blocked. * @param callScreeningAppName The call screening application name which block the call. * @param callScreeningComponentName The call screening component name which block the call. - * @param callIdPackageName The package name of the - * {@link android.telecom.CallScreeningService} which provided - * {@link CallIdentification}. - * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService} - * which provided {@link CallIdentification}. - * @param callIdName The caller name provided by the - * {@link android.telecom.CallScreeningService}. - * @param callIdDescription The caller description provided by the - * {@link android.telecom.CallScreeningService}. - * @param callIdDetails The caller details provided by the - * {@link android.telecom.CallScreeningService}. - * @param callIdCallType The caller type provided by the - * {@link android.telecom.CallScreeningService}. * * @result The URI of the call log entry belonging to the user that made or received this * call. This could be of the shadow provider. Do not return it to non-system apps, @@ -803,37 +790,10 @@ public class CallLog { number, userToBeInsertedTo, addForAllUsers)); } final ContentResolver resolver = context.getContentResolver(); - int numberPresentation = PRESENTATION_ALLOWED; - TelecomManager tm = null; - try { - tm = TelecomManager.from(context); - } catch (UnsupportedOperationException e) {} - - String accountAddress = null; - if (tm != null && accountHandle != null) { - PhoneAccount account = tm.getPhoneAccount(accountHandle); - if (account != null) { - Uri address = account.getSubscriptionAddress(); - if (address != null) { - accountAddress = address.getSchemeSpecificPart(); - } - } - } + String accountAddress = getLogAccountAddress(context, accountHandle); - // Remap network specified number presentation types - // PhoneConstants.PRESENTATION_xxx to calllog number presentation types - // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog - // from any future radio changes. - // If the number field is empty set the presentation type to Unknown. - if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { - numberPresentation = PRESENTATION_RESTRICTED; - } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { - numberPresentation = PRESENTATION_PAYPHONE; - } else if (TextUtils.isEmpty(number) - || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { - numberPresentation = PRESENTATION_UNKNOWN; - } + int numberPresentation = getLogNumberPresentation(number, presentation); if (numberPresentation != PRESENTATION_ALLOWED) { number = ""; if (ci != null) { @@ -1138,8 +1098,7 @@ public class CallLog { if (TextUtils.isEmpty(countryIso)) { return; } - final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, - getCurrentCountryIso(context)); + final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso); if (TextUtils.isEmpty(normalizedNumber)) { return; } @@ -1148,6 +1107,54 @@ public class CallLog { resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); } + /** + * Remap network specified number presentation types + * PhoneConstants.PRESENTATION_xxx to calllog number presentation types + * Calls.PRESENTATION_xxx, in order to insulate the persistent calllog + * from any future radio changes. + * If the number field is empty set the presentation type to Unknown. + */ + private static int getLogNumberPresentation(String number, int presentation) { + if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { + return presentation; + } + + if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { + return presentation; + } + + if (TextUtils.isEmpty(number) + || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { + return PRESENTATION_UNKNOWN; + } + + return PRESENTATION_ALLOWED; + } + + private static String getLogAccountAddress(Context context, + PhoneAccountHandle accountHandle) { + TelecomManager tm = null; + try { + tm = TelecomManager.from(context); + } catch (UnsupportedOperationException e) { + if (VERBOSE_LOG) { + Log.v(LOG_TAG, "No TelecomManager found to get account address."); + } + } + + String accountAddress = null; + if (tm != null && accountHandle != null) { + PhoneAccount account = tm.getPhoneAccount(accountHandle); + if (account != null) { + Uri address = account.getSubscriptionAddress(); + if (address != null) { + accountAddress = address.getSchemeSpecificPart(); + } + } + } + return accountAddress; + } + private static String getCurrentCountryIso(Context context) { String countryIso = null; final CountryDetector detector = (CountryDetector) context.getSystemService( diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 5ac31dc830ce..41d3cbb88f5c 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -72,45 +72,13 @@ public final class DeviceConfig { public static final String NAMESPACE_AUTOFILL = "autofill"; /** - * ContentCapture-related properties definitions. - * - * @hide - */ - @SystemApi - public interface ContentCapture { - String NAMESPACE = "content_capture"; - - /** - * Property used by {@code com.android.server.SystemServer} on start to decide whether - * the Content Capture service should be created or not. - * - * <p>Possible values are: - * - * <ul> - * <li>If set to {@code default}, it will only be set if the OEM provides and defines the - * service name by overlaying {@code config_defaultContentCaptureService} (this is the - * "default" mode) - * <li>If set to {@code always}, it will always be enabled, even when the resource is not - * overlaid (this is useful during development and to run the CTS tests on AOSP builds). - * <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs - * can disable it remotely in case of emergency by setting to something else (like - * {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one - * of the values above). - * </ul> - * - * @hide - */ - // TODO(b/121153631): revert back to SERVICE_EXPLICITLY_ENABLED approach - String PROPERTY_CONTENTCAPTURE_ENABLED = "enable_contentcapture"; - } - - /** * Namespace for content capture feature used by on-device machine intelligence * to provide suggestions in a privacy-safe manner. * * @hide */ @SystemApi + @TestApi public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; /** @@ -154,21 +122,6 @@ public final class DeviceConfig { } /** - * Namespace for all runtime related features. - * - * @hide - */ - @SystemApi - public interface Runtime { - String NAMESPACE = "runtime"; - - /** - * Whether or not we use the precompiled layout. - */ - String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled"; - } - - /** * Namespace for all runtime native related features. * * @hide @@ -375,6 +328,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static String getProperty(String namespace, String name) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 67c840006948..0b3842080a75 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -157,10 +157,6 @@ public final class MediaStore { public static final String PARAM_DELETE_DATA = "deletedata"; /** {@hide} */ - public static final String PARAM_PRIMARY = "primary"; - /** {@hide} */ - public static final String PARAM_SECONDARY = "secondary"; - /** {@hide} */ public static final String PARAM_INCLUDE_PENDING = "includePending"; /** {@hide} */ public static final String PARAM_INCLUDE_TRASHED = "includeTrashed"; @@ -543,7 +539,9 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) + * @removed */ + @Deprecated public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) { return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build(); } @@ -580,14 +578,7 @@ public final class MediaStore { */ public static @NonNull Uri createPending(@NonNull Context context, @NonNull PendingParams params) { - final Uri.Builder builder = params.insertUri.buildUpon(); - if (!TextUtils.isEmpty(params.primaryDirectory)) { - builder.appendQueryParameter(PARAM_PRIMARY, params.primaryDirectory); - } - if (!TextUtils.isEmpty(params.secondaryDirectory)) { - builder.appendQueryParameter(PARAM_SECONDARY, params.secondaryDirectory); - } - return context.getContentResolver().insert(builder.build(), params.insertValues); + return context.getContentResolver().insert(params.insertUri, params.insertValues); } /** @@ -610,10 +601,6 @@ public final class MediaStore { public final Uri insertUri; /** {@hide} */ public final ContentValues insertValues; - /** {@hide} */ - public String primaryDirectory; - /** {@hide} */ - public String secondaryDirectory; /** * Create parameters that describe a pending media item. @@ -655,7 +642,11 @@ public final class MediaStore { * @see MediaColumns#PRIMARY_DIRECTORY */ public void setPrimaryDirectory(@Nullable String primaryDirectory) { - this.primaryDirectory = primaryDirectory; + if (primaryDirectory == null) { + this.insertValues.remove(MediaColumns.PRIMARY_DIRECTORY); + } else { + this.insertValues.put(MediaColumns.PRIMARY_DIRECTORY, primaryDirectory); + } } /** @@ -668,7 +659,11 @@ public final class MediaStore { * @see MediaColumns#SECONDARY_DIRECTORY */ public void setSecondaryDirectory(@Nullable String secondaryDirectory) { - this.secondaryDirectory = secondaryDirectory; + if (secondaryDirectory == null) { + this.insertValues.remove(MediaColumns.SECONDARY_DIRECTORY); + } else { + this.insertValues.put(MediaColumns.SECONDARY_DIRECTORY, secondaryDirectory); + } } /** @@ -797,7 +792,9 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) + * @removed */ + @Deprecated public static void trash(@NonNull Context context, @NonNull Uri uri) { trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS); } @@ -815,7 +812,9 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) + * @removed */ + @Deprecated public static void trash(@NonNull Context context, @NonNull Uri uri, @DurationMillisLong long timeoutMillis) { if (timeoutMillis < 0) { @@ -837,7 +836,9 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) + * @removed */ + @Deprecated public static void untrash(@NonNull Context context, @NonNull Uri uri) { final ContentValues values = new ContentValues(); values.put(MediaColumns.IS_TRASHED, 0); @@ -884,7 +885,9 @@ public final class MediaStore { * hash is calculated. * <p> * Type: BLOB + * @removed */ + @Deprecated public static final String HASH = "_hash"; /** @@ -921,8 +924,22 @@ public final class MediaStore { public static final String DATE_MODIFIED = "date_modified"; /** - * The MIME type of the file - * <P>Type: TEXT</P> + * The MIME type of the media item. + * <p> + * This is typically defined based on the file extension of the media + * item. However, it may be the value of the {@code format} attribute + * defined by the <em>Dublin Core Media Initiative</em> standard, + * extracted from any XMP metadata contained within this media item. + * <p class="note"> + * Note: the {@code format} attribute may be ignored if the top-level + * MIME type disagrees with the file extension. For example, it's + * reasonable for an {@code image/jpeg} file to declare a {@code format} + * of {@code image/vnd.google.panorama360+jpg}, but declaring a + * {@code format} of {@code audio/ogg} would be ignored. + * <p> + * This is a read-only column that is automatically computed. + * <p> + * Type: TEXT */ public static final String MIME_TYPE = "mime_type"; @@ -965,7 +982,9 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) + * @removed */ + @Deprecated public static final String IS_TRASHED = "is_trashed"; /** @@ -974,7 +993,9 @@ public final class MediaStore { * {@link #IS_PENDING} or {@link #IS_TRASHED}. * <p> * Type: INTEGER + * @removed */ + @Deprecated public static final String DATE_EXPIRES = "date_expires"; /** @@ -991,6 +1012,8 @@ public final class MediaStore { * Package name that contributed this media. The value may be * {@code NULL} if ownership cannot be reliably determined. * <p> + * This is a read-only column that is automatically computed. + * <p> * Type: TEXT */ public static final String OWNER_PACKAGE_NAME = "owner_package_name"; @@ -1014,6 +1037,52 @@ public final class MediaStore { * @see PendingParams#setSecondaryDirectory(String) */ public static final String SECONDARY_DIRECTORY = "secondary_directory"; + + /** + * The "document ID" GUID as defined by the <em>XMP Media + * Management</em> standard, extracted from any XMP metadata contained + * within this media item. The value is {@code null} when no metadata + * was found. + * <p> + * Each "document ID" is created once for each new resource. Different + * renditions of that resource are expected to have different IDs. + * <p> + * This is a read-only column that is automatically computed. + * <p> + * Type: TEXT + */ + public static final String DOCUMENT_ID = "document_id"; + + /** + * The "instance ID" GUID as defined by the <em>XMP Media + * Management</em> standard, extracted from any XMP metadata contained + * within this media item. The value is {@code null} when no metadata + * was found. + * <p> + * This "instance ID" changes with each save operation of a specific + * "document ID". + * <p> + * This is a read-only column that is automatically computed. + * <p> + * Type: TEXT + */ + public static final String INSTANCE_ID = "instance_id"; + + /** + * The "original document ID" GUID as defined by the <em>XMP Media + * Management</em> standard, extracted from any XMP metadata contained + * within this media item. + * <p> + * This "original document ID" links a resource to its original source. + * For example, when you save a PSD document as a JPEG, then convert the + * JPEG to GIF format, the "original document ID" of both the JPEG and + * GIF files is the "document ID" of the original PSD file. + * <p> + * This is a read-only column that is automatically computed. + * <p> + * Type: TEXT + */ + public static final String ORIGINAL_DOCUMENT_ID = "original_document_id"; } /** @@ -3097,20 +3166,29 @@ public final class MediaStore { final ArrayList<File> res = new ArrayList<>(); if (VOLUME_INTERNAL.equals(volumeName)) { - res.add(new File(Environment.getRootDirectory(), "media")); - res.add(new File(Environment.getOemDirectory(), "media")); - res.add(new File(Environment.getProductDirectory(), "media")); + addCanoncialFile(res, new File(Environment.getRootDirectory(), "media")); + addCanoncialFile(res, new File(Environment.getOemDirectory(), "media")); + addCanoncialFile(res, new File(Environment.getProductDirectory(), "media")); } else { - res.add(getVolumePath(volumeName)); + addCanoncialFile(res, getVolumePath(volumeName)); final UserManager um = AppGlobals.getInitialApplication() .getSystemService(UserManager.class); if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) { - res.add(Environment.getDataPreloadsMediaDirectory()); + addCanoncialFile(res, Environment.getDataPreloadsMediaDirectory()); } } return res; } + private static void addCanoncialFile(List<File> list, File file) { + try { + list.add(file.getCanonicalFile()); + } catch (IOException e) { + Log.w(TAG, "Failed to resolve " + file + ": " + e); + list.add(file); + } + } + /** * Uri for querying the state of the media scanner. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 90749bb0183b..de84e713a6f7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8289,11 +8289,22 @@ public final class Settings { * The value is boolean (1 or 0). * @hide */ + @TestApi public static final String NOTIFICATION_BADGING = "notification_badging"; private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR; /** + * Whether the notification bubbles are globally enabled + * The value is boolean (1 or 0). + * @hide + */ + @TestApi + public static final String NOTIFICATION_BUBBLES = "notification_bubbles"; + + private static final Validator NOTIFICATION_BUBBLES_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right * swipe). * @@ -8604,6 +8615,7 @@ public final class Settings { ASSIST_GESTURE_WAKE_ENABLED, VR_DISPLAY_MODE, NOTIFICATION_BADGING, + NOTIFICATION_BUBBLES, NOTIFICATION_DISMISS_RTL, QS_AUTO_ADDED_TILES, SCREENSAVER_ENABLED, @@ -8766,6 +8778,7 @@ public final class Settings { VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR); VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR); VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR); + VALIDATORS.put(NOTIFICATION_BUBBLES, NOTIFICATION_BUBBLES_VALIDATOR); VALIDATORS.put(NOTIFICATION_DISMISS_RTL, NOTIFICATION_DISMISS_RTL_VALIDATOR); VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR); VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR); @@ -9288,6 +9301,13 @@ public final class Settings { public static final String DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes"; /** + * Which application package is allowed to save View attribute data. + * @hide + */ + public static final String DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE = + "debug_view_attributes_application_package"; + + /** * Whether assisted GPS should be enabled or not. * @hide */ @@ -14010,6 +14030,7 @@ public final class Settings { INSTANT_APP_SETTINGS.add(TRANSITION_ANIMATION_SCALE); INSTANT_APP_SETTINGS.add(ANIMATOR_DURATION_SCALE); INSTANT_APP_SETTINGS.add(DEBUG_VIEW_ATTRIBUTES); + INSTANT_APP_SETTINGS.add(DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE); INSTANT_APP_SETTINGS.add(WTF_IS_FATAL); INSTANT_APP_SETTINGS.add(SEND_ACTION_APP_ERROR); INSTANT_APP_SETTINGS.add(ZEN_MODE); diff --git a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl index 4e98201118a4..40852ea9719d 100644 --- a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl +++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl @@ -23,13 +23,13 @@ import android.app.role.IRoleManagerCallback; */ oneway interface IRoleControllerService { - void onAddRoleHolder(in String roleName, in String packageName, + void onAddRoleHolder(in String roleName, in String packageName, int flags, in IRoleManagerCallback callback); - void onRemoveRoleHolder(in String roleName, in String packageName, + void onRemoveRoleHolder(in String roleName, in String packageName, int flags, in IRoleManagerCallback callback); - void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback); + void onClearRoleHolders(in String roleName, int flags, in IRoleManagerCallback callback); void onGrantDefaultRoles(in IRoleManagerCallback callback); diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java index 5403cfa06807..c846b076c14f 100644 --- a/core/java/android/rolecontrollerservice/RoleControllerService.java +++ b/core/java/android/rolecontrollerservice/RoleControllerService.java @@ -61,32 +61,33 @@ public abstract class RoleControllerService extends Service { return new IRoleControllerService.Stub() { @Override - public void onAddRoleHolder(String roleName, String packageName, + public void onAddRoleHolder(String roleName, String packageName, int flags, IRoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - RoleControllerService.this.onAddRoleHolder(roleName, packageName, + RoleControllerService.this.onAddRoleHolder(roleName, packageName, flags, new RoleManagerCallbackDelegate(callback)); } @Override - public void onRemoveRoleHolder(String roleName, String packageName, + public void onRemoveRoleHolder(String roleName, String packageName, int flags, IRoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - RoleControllerService.this.onRemoveRoleHolder(roleName, packageName, + RoleControllerService.this.onRemoveRoleHolder(roleName, packageName, flags, new RoleManagerCallbackDelegate(callback)); } @Override - public void onClearRoleHolders(String roleName, IRoleManagerCallback callback) { + public void onClearRoleHolders(String roleName, int flags, + IRoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - RoleControllerService.this.onClearRoleHolders(roleName, + RoleControllerService.this.onClearRoleHolders(roleName, flags, new RoleManagerCallbackDelegate(callback)); } @@ -113,37 +114,41 @@ public abstract class RoleControllerService extends Service { * * @param roleName the name of the role to add the role holder for * @param packageName the package name of the application to add to the role holders + * @param flags optional behavior flags * @param callback the callback for whether this call is successful * - * @see RoleManager#addRoleHolderAsUser(String, String, UserHandle, Executor, + * @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor, * RoleManagerCallback) */ public abstract void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, - @NonNull RoleManagerCallback callback); + @RoleManager.ManageHoldersFlags int flags, @NonNull RoleManagerCallback callback); /** * Remove a specific application from the holders of a role. * * @param roleName the name of the role to remove the role holder for * @param packageName the package name of the application to remove from the role holders + * @param flags optional behavior flags * @param callback the callback for whether this call is successful * - * @see RoleManager#removeRoleHolderAsUser(String, String, UserHandle, Executor, + * @see RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, Executor, * RoleManagerCallback) */ public abstract void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, - @NonNull RoleManagerCallback callback); + @RoleManager.ManageHoldersFlags int flags, @NonNull RoleManagerCallback callback); /** * Remove all holders of a role. * * @param roleName the name of the role to remove role holders for + * @param flags optional behavior flags * @param callback the callback for whether this call is successful * - * @see RoleManager#clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback) + * @see RoleManager#clearRoleHoldersAsUser(String, int, UserHandle, Executor, + * RoleManagerCallback) */ public abstract void onClearRoleHolders(@NonNull String roleName, - @NonNull RoleManagerCallback callback); + @RoleManager.ManageHoldersFlags int flags, @NonNull RoleManagerCallback callback); /** * Cleanup appop/permissions state in response to sms kill switch toggle diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 8e0f522b5665..81d066d3d656 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -234,7 +234,12 @@ public abstract class AugmentedAutofillService extends Service { } /** - * Implementation specific {@code dump}. + * Implementation specific {@code dump}. The child class can override the method to provide + * additional information about the Service's state into the dumpsys output. + * + * @param pw The PrintWriter to which you should dump your state. This will be closed for + * you after you return. + * @param args additional arguments to the dump request. */ protected void dump(@NonNull PrintWriter pw, @SuppressWarnings("unused") @NonNull String[] args) { diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/service/carrier/CarrierMessagingClientService.java index 3829d7103b07..13f4fc4b0dcb 100644 --- a/core/java/android/app/SmsAppService.java +++ b/core/java/android/service/carrier/CarrierMessagingClientService.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.app; +package android.service.carrier; +import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; @@ -24,10 +25,11 @@ import android.os.IBinder; * it so that the process is always running, which allows the app to have a persistent connection * to the server. * - * <p>The service must have an {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE} + * <p>The service must have an + * {@link android.telephony.TelephonyManager#ACTION_CARRIER_MESSAGING_CLIENT_SERVICE} * action in the intent handler, and be protected with - * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to - * be exported. + * {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_CLIENT_SERVICE}. + * However the service does not have to be exported. * * <p>The service must be associated with a non-main process, meaning it must have an * {@code android:process} tag in its manifest entry. @@ -45,27 +47,27 @@ import android.os.IBinder; * * <p>Example: First, define a subclass in the application: * <pre> - * public class MySmsAppService extends SmsAppService { + * public class MyCarrierMessagingClientService extends CarrierMessagingClientService { * } * </pre> * Then, declare it in its {@code AndroidManifest.xml}: * <pre> * <service - * android:name=".MySmsAppService" + * android:name=".MyCarrierMessagingClientService" * android:exported="false" * android:process=":persistent" - * android:permission="android.permission.BIND_SMS_APP_SERVICE"> + * android:permission="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"> * <intent-filter> - * <action android:name="android.telephony.action.SMS_APP_SERVICE" /> + * <action android:name="android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE" /> * </intent-filter> * </service> * </pre> */ -public class SmsAppService extends Service { - private final ISmsAppService mImpl; +public class CarrierMessagingClientService extends Service { + private final ICarrierMessagingClientServiceImpl mImpl; - public SmsAppService() { - mImpl = new ISmsAppServiceImpl(); + public CarrierMessagingClientService() { + mImpl = new ICarrierMessagingClientServiceImpl(); } @Override @@ -73,6 +75,6 @@ public class SmsAppService extends Service { return mImpl.asBinder(); } - private class ISmsAppServiceImpl extends ISmsAppService.Stub { + private class ICarrierMessagingClientServiceImpl extends ICarrierMessagingClientService.Stub { } } diff --git a/core/java/android/app/ISmsAppService.aidl b/core/java/android/service/carrier/ICarrierMessagingClientService.aidl index 1ac2ec6b1c41..dbe7d121d56e 100644 --- a/core/java/android/app/ISmsAppService.aidl +++ b/core/java/android/service/carrier/ICarrierMessagingClientService.aidl @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.app; +package android.service.carrier; /** * @hide */ -interface ISmsAppService { +interface ICarrierMessagingClientService { } diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java index eeb340b67e68..efb8923a459f 100644 --- a/core/java/android/service/dreams/Sandman.java +++ b/core/java/android/service/dreams/Sandman.java @@ -91,8 +91,9 @@ public final class Sandman { // and the UI mode manager starting a dream. We want the system to already // be awake by the time this happens. Otherwise the dream may not start. PowerManager powerManager = - (PowerManager)context.getSystemService(Context.POWER_SERVICE); + context.getSystemService(PowerManager.class); powerManager.wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_PLUGGED_IN, "android.service.dreams:DREAM"); } else { Slog.i(TAG, "Activating dream by user request."); diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index d4e887970a47..d3285bb53eb3 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1503,6 +1503,7 @@ public abstract class NotificationListenerService extends Service { private boolean mNoisy; private ArrayList<Notification.Action> mSmartActions; private ArrayList<CharSequence> mSmartReplies; + private boolean mCanBubble; public Ranking() {} @@ -1677,6 +1678,17 @@ public abstract class NotificationListenerService extends Service { return mLastAudiblyAlertedMs; } + /** + * Returns whether the user has allowed bubbles globally, at the app level, and at the + * channel level for this notification. + * + * <p>This does not take into account the current importance of the notification, the + * current DND state, or whether the posting app is foreground.</p> + */ + public boolean canBubble() { + return mCanBubble; + } + /** @hide */ public boolean isNoisy() { return mNoisy; @@ -1693,7 +1705,7 @@ public abstract class NotificationListenerService extends Service { ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge, int userSentiment, boolean hidden, long lastAudiblyAlertedMs, boolean noisy, ArrayList<Notification.Action> smartActions, - ArrayList<CharSequence> smartReplies) { + ArrayList<CharSequence> smartReplies, boolean canBubble) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1713,6 +1725,7 @@ public abstract class NotificationListenerService extends Service { mNoisy = noisy; mSmartActions = smartActions; mSmartReplies = smartReplies; + mCanBubble = canBubble; } /** @@ -1766,6 +1779,7 @@ public abstract class NotificationListenerService extends Service { private ArrayMap<String, Boolean> mNoisy; private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions; private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies; + private boolean[] mCanBubble; private RankingMap(NotificationRankingUpdate rankingUpdate) { mRankingUpdate = rankingUpdate; @@ -1796,7 +1810,7 @@ public abstract class NotificationListenerService extends Service { getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), getShowBadge(key), getUserSentiment(key), getHidden(key), getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key), - getSmartReplies(key)); + getSmartReplies(key), canBubble(key)); return rank >= 0; } @@ -1972,6 +1986,19 @@ public abstract class NotificationListenerService extends Service { return mSmartReplies.get(key); } + private boolean canBubble(String key) { + synchronized (this) { + if (mRanks == null) { + buildRanksLocked(); + } + if (mCanBubble == null) { + mCanBubble = mRankingUpdate.getCanBubble(); + } + } + int keyIndex = mRanks.getOrDefault(key, -1); + return keyIndex >= 0 ? mCanBubble[keyIndex] : false; + } + // Locked by 'this' private void buildRanksLocked() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index ebaeff8ff33c..230ae2713554 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -41,13 +41,14 @@ public class NotificationRankingUpdate implements Parcelable { private final Bundle mSmartReplies; private final Bundle mLastAudiblyAlerted; private final Bundle mNoisy; + private final boolean[] mCanBubble; public NotificationRankingUpdate(String[] keys, String[] interceptedKeys, Bundle visibilityOverrides, Bundle suppressedVisualEffects, int[] importance, Bundle explanation, Bundle overrideGroupKeys, Bundle channels, Bundle overridePeople, Bundle snoozeCriteria, Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions, - Bundle smartReplies, Bundle lastAudiblyAlerted, Bundle noisy) { + Bundle smartReplies, Bundle lastAudiblyAlerted, Bundle noisy, boolean[] canBubble) { mKeys = keys; mInterceptedKeys = interceptedKeys; mVisibilityOverrides = visibilityOverrides; @@ -65,6 +66,7 @@ public class NotificationRankingUpdate implements Parcelable { mSmartReplies = smartReplies; mLastAudiblyAlerted = lastAudiblyAlerted; mNoisy = noisy; + mCanBubble = canBubble; } public NotificationRankingUpdate(Parcel in) { @@ -86,6 +88,8 @@ public class NotificationRankingUpdate implements Parcelable { mSmartReplies = in.readBundle(); mLastAudiblyAlerted = in.readBundle(); mNoisy = in.readBundle(); + mCanBubble = new boolean[mKeys.length]; + in.readBooleanArray(mCanBubble); } @Override @@ -112,6 +116,7 @@ public class NotificationRankingUpdate implements Parcelable { out.writeBundle(mSmartReplies); out.writeBundle(mLastAudiblyAlerted); out.writeBundle(mNoisy); + out.writeBooleanArray(mCanBubble); } public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR @@ -192,4 +197,8 @@ public class NotificationRankingUpdate implements Parcelable { public Bundle getNoisy() { return mNoisy; } + + public boolean[] getCanBubble() { + return mCanBubble; + } } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 949328fd3a99..915a18e59226 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -94,7 +94,8 @@ public class TextLine { private final DecorationInfo mDecorationInfo = new DecorationInfo(); private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>(); - @UnsupportedAppUsage + /** Not allowed to access. If it's for memory leak workaround, it was already fixed M. */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static final TextLine[] sCached = new TextLine[3]; /** diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 59e562fe8071..62ed901c5e06 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -40,6 +40,7 @@ import android.util.Slog; import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeIdManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; @@ -154,13 +155,7 @@ public final class AccessibilityInteractionController { } private boolean isShown(View view) { - // The first two checks are made also made by isShown() which - // however traverses the tree up to the parent to catch that. - // Therefore, we do some fail fast check to minimize the up - // tree traversal. - return (view.mAttachInfo != null - && view.mAttachInfo.mWindowVisibility == View.VISIBLE - && view.isShown()); + return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown()); } public void findAccessibilityNodeInfoByAccessibilityIdClientThread( @@ -340,13 +335,8 @@ public final class AccessibilityInteractionController { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - View root = null; - if (accessibilityViewId == AccessibilityNodeInfo.ROOT_ITEM_ID) { - root = mViewRootImpl.mView; - } else { - root = findViewByAccessibilityId(accessibilityViewId); - } - if (root != null && isShown(root)) { + final View root = findViewByAccessibilityId(accessibilityViewId); + if (root != null) { mPrefetcher.prefetchAccessibilityNodeInfos( root, virtualDescendantId, flags, infos, arguments); } @@ -396,12 +386,7 @@ public final class AccessibilityInteractionController { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) { - root = findViewByAccessibilityId(accessibilityViewId); - } else { - root = mViewRootImpl.mView; - } + final View root = findViewByAccessibilityId(accessibilityViewId); if (root != null) { final int resolvedViewId = root.getContext().getResources() .getIdentifier(viewId, null, null); @@ -462,13 +447,8 @@ public final class AccessibilityInteractionController { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) { - root = findViewByAccessibilityId(accessibilityViewId); - } else { - root = mViewRootImpl.mView; - } - if (root != null && isShown(root)) { + final View root = findViewByAccessibilityId(accessibilityViewId); + if (root != null) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { infos = provider.findAccessibilityNodeInfosByText(text, @@ -550,13 +530,8 @@ public final class AccessibilityInteractionController { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) { - root = findViewByAccessibilityId(accessibilityViewId); - } else { - root = mViewRootImpl.mView; - } - if (root != null && isShown(root)) { + final View root = findViewByAccessibilityId(accessibilityViewId); + if (root != null) { switch (focusType) { case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: { View host = mViewRootImpl.mAccessibilityFocusedHost; @@ -583,7 +558,7 @@ public final class AccessibilityInteractionController { } break; case AccessibilityNodeInfo.FOCUS_INPUT: { View target = root.findFocus(); - if (target == null || !isShown(target)) { + if (!isShown(target)) { break; } AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); @@ -645,13 +620,8 @@ public final class AccessibilityInteractionController { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) { - root = findViewByAccessibilityId(accessibilityViewId); - } else { - root = mViewRootImpl.mView; - } - if (root != null && isShown(root)) { + final View root = findViewByAccessibilityId(accessibilityViewId); + if (root != null) { View nextView = root.focusSearch(direction); if (nextView != null) { next = nextView.createAccessibilityNodeInfo(); @@ -705,13 +675,8 @@ public final class AccessibilityInteractionController { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - View target = null; - if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) { - target = findViewByAccessibilityId(accessibilityViewId); - } else { - target = mViewRootImpl.mView; - } - if (target != null && isShown(target)) { + final View target = findViewByAccessibilityId(accessibilityViewId); + if (target != null) { if (action == R.id.accessibilityActionClickOnClickableSpan) { // Handle this hidden action separately succeeded = handleClickableSpanActionUiThread( @@ -791,15 +756,13 @@ public final class AccessibilityInteractionController { } private View findViewByAccessibilityId(int accessibilityId) { - View root = mViewRootImpl.mView; - if (root == null) { - return null; - } - View foundView = root.findViewByAccessibilityId(accessibilityId); - if (foundView != null && !isShown(foundView)) { - return null; + if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) { + return mViewRootImpl.mView; + } else { + final View foundView = + AccessibilityNodeIdManager.getInstance().findView(accessibilityId); + return isShown(foundView) ? foundView : null; } - return foundView; } private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos, @@ -1171,7 +1134,7 @@ public final class AccessibilityInteractionController { } View child = children.get(i); if (child.getAccessibilityViewId() != current.getAccessibilityViewId() - && isShown(child)) { + && isShown(child)) { AccessibilityNodeInfo info = null; AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider(); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index ccd0fc179f0e..c64386e8db79 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -22,7 +22,6 @@ import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.graphics.FrameInfo; -import android.graphics.Insets; import android.hardware.display.DisplayManagerGlobal; import android.os.Build; import android.os.Handler; @@ -134,7 +133,7 @@ public final class Choreographer { }; // Enable/disable vsync for animations and drawing. - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769497) private static final boolean USE_VSYNC = SystemProperties.getBoolean( "debug.choreographer.vsync", true); @@ -914,25 +913,11 @@ public final class Choreographer { super(looper, vsyncSource); } + // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for + // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC + // for the internal display implicitly. @Override - public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { - // Ignore vsync from secondary display. - // This can be problematic because the call to scheduleVsync() is a one-shot. - // We need to ensure that we will still receive the vsync from the primary - // display which is the one we really care about. Ideally we should schedule - // vsync for a particular display. - // At this time Surface Flinger won't send us vsyncs for secondary displays - // but that could change in the future so let's log a message to help us remember - // that we need to fix this. - if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { - Log.d(TAG, "Received vsync from secondary display, but we don't support " - + "this case yet. Choreographer needs a way to explicitly request " - + "vsync for a specific display to ensure it doesn't lose track " - + "of its scheduled vsync."); - scheduleVsync(); - return; - } - + public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { // Post the vsync event to the Handler. // The idea is to prevent incoming vsync events from completely starving // the message queue. If there are no messages in the queue with timestamps diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index e3a6bd7a6949..49bae280321e 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -32,6 +32,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -495,7 +496,7 @@ public final class Display { * @return True if the display is still valid. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public boolean getDisplayInfo(DisplayInfo outDisplayInfo) { synchronized (this) { updateDisplayInfoLocked(); diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index edd3f1a8de98..3e8002f4634d 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -136,12 +136,11 @@ public abstract class DisplayEventReceiver { * * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()} * timebase. - * @param builtInDisplayId The surface flinger built-in display id such as - * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}. + * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param frame The frame number. Increases by one for each vertical sync interval. */ @UnsupportedAppUsage - public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { + public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { } /** @@ -149,12 +148,11 @@ public abstract class DisplayEventReceiver { * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. - * @param builtInDisplayId The surface flinger built-in display id such as - * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_HDMI}. + * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param connected True if the display is connected, false if it disconnected. */ @UnsupportedAppUsage - public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { + public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { } /** @@ -174,14 +172,14 @@ public abstract class DisplayEventReceiver { // Called from native code. @SuppressWarnings("unused") @UnsupportedAppUsage - private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) { - onVsync(timestampNanos, builtInDisplayId, frame); + private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) { + onVsync(timestampNanos, physicalDisplayId, frame); } // Called from native code. @SuppressWarnings("unused") @UnsupportedAppUsage - private void dispatchHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { - onHotplug(timestampNanos, builtInDisplayId, connected); + private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { + onHotplug(timestampNanos, physicalDisplayId, connected); } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 43de1f89649c..ad8fee978c9a 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -27,6 +27,7 @@ import android.annotation.UnsupportedAppUsage; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; @@ -157,8 +158,9 @@ public final class DisplayInfo implements Parcelable { * * @hide */ + // Remark on @UnsupportedAppUsage: Display.getCutout should be used instead @Nullable - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public DisplayCutout displayCutout; /** @@ -281,7 +283,7 @@ public final class DisplayInfo implements Parcelable { } }; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769467) public DisplayInfo() { } diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 03d99553f125..3e749f4ba16f 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -37,7 +37,7 @@ public abstract class DisplayListCanvas extends BaseRecordingCanvas { } /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top, CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx, CanvasProperty<Float> ry, CanvasProperty<Paint> paint); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 8ae4757f5de6..2ef7c4b16d9d 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -25,6 +25,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -380,6 +381,16 @@ interface IWindowManager void getStableInsets(int displayId, out Rect outInsets); /** + * Set the forwarded insets on the display. + * <p> + * This is only used in case a virtual display is displayed on another display that has insets, + * and the bounds of the virtual display is overlapping with the insets from the host display. + * In that case, the contents on the virtual display won't be placed over the forwarded insets. + * Only the owner of the display is permitted to set the forwarded insets on it. + */ + void setForwardedInsets(int displayId, in Insets insets); + + /** * Register shortcut key. Shortcut code is packed as: * (MetaState << Integer.SIZE) | KeyCode * @hide diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 583651dee379..dd88e3c34d30 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -33,6 +33,7 @@ import android.util.SparseSetArray; import android.view.InsetsState.InsetSide; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowInsets.Type.InsetType; +import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.VisibleForTesting; @@ -165,7 +166,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll @Nullable @InsetSide SparseIntArray typeSideMap) { return state.calculateInsets(frame, false /* isScreenRound */, false /* alwaysConsumerNavBar */, null /* displayCutout */, - null /* legacyContentInsets */, null /* legacyStableInsets */, typeSideMap) + null /* legacyContentInsets */, null /* legacyStableInsets */, + LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, typeSideMap) .getInsets(mTypes); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7ad97a6d393e..258600019e71 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -112,6 +112,8 @@ public class InsetsController implements WindowInsetsController { private int mPendingTypesToShow; + private int mLastLegacySoftInputMode; + public InsetsController(ViewRootImpl viewRoot) { mViewRoot = viewRoot; mAnimCallback = () -> { @@ -126,13 +128,17 @@ public class InsetsController implements WindowInsetsController { } WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(), - mLastLegacyContentInsets, mLastLegacyStableInsets, + mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode, null /* typeSideMap */); mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets); }; } void onFrameChanged(Rect frame) { + if (mFrame.equals(frame)) { + return; + } + mViewRoot.notifyInsetsChanged(); mFrame.set(frame); } @@ -160,11 +166,12 @@ public class InsetsController implements WindowInsetsController { @VisibleForTesting public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeNavBar, DisplayCutout cutout, Rect legacyContentInsets, - Rect legacyStableInsets) { + Rect legacyStableInsets, int legacySoftInputMode) { mLastLegacyContentInsets.set(legacyContentInsets); mLastLegacyStableInsets.set(legacyStableInsets); + mLastLegacySoftInputMode = legacySoftInputMode; mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout, - legacyContentInsets, legacyStableInsets, + legacyContentInsets, legacyStableInsets, legacySoftInputMode, null /* typeSideMap */); return mLastInsets; } @@ -257,11 +264,21 @@ public class InsetsController implements WindowInsetsController { private void controlWindowInsetsAnimation(@InsetType int types, WindowInsetsAnimationControlListener listener, boolean fromIme) { + // If the frame of our window doesn't span the entire display, the control API makes very + // little sense, as we don't deal with negative insets. So just cancel immediately. + if (!mState.getDisplayFrame().equals(mFrame)) { + listener.onCancelled(); + return; + } + controlAnimationUnchecked(types, listener, mFrame, fromIme); + } + + private void controlAnimationUnchecked(@InsetType int types, + WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) { if (types == 0) { // nothing to animate. return; } - // TODO: Check whether we already have a controller. final ArraySet<Integer> internalTypes = mState.toInternalType(types); final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); @@ -285,7 +302,7 @@ public class InsetsController implements WindowInsetsController { } final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers, - mFrame, mState, listener, typesReady, + frame, mState, listener, typesReady, () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this); mAnimationControls.add(controller); } @@ -436,6 +453,7 @@ public class InsetsController implements WindowInsetsController { // nothing to animate. return; } + WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() { @Override public void onReady(WindowInsetsAnimationController controller, int types) { @@ -479,7 +497,10 @@ public class InsetsController implements WindowInsetsController { // TODO: Instead of clearing this here, properly wire up // InsetsAnimationControlImpl.finish() to remove this from mAnimationControls. mAnimationControls.clear(); - controlWindowInsetsAnimation(types, listener, fromIme); + + // Show/hide animations always need to be relative to the display frame, in order that shown + // and hidden state insets are correct. + controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme); } private void hideDirectly(@InsetType int types) { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 4f809fe6d54a..69f86aa5b37e 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -19,6 +19,7 @@ package android.view; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; +import static android.view.WindowInsets.Type.IME; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.indexOf; @@ -34,11 +35,13 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetType; +import android.view.WindowManager.LayoutParams; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Objects; /** * Holder for state of system windows that cause window insets for all other windows in the system. @@ -104,6 +107,11 @@ public class InsetsState implements Parcelable { private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>(); + /** + * The frame of the display these sources are relative to. + */ + private final Rect mDisplayFrame = new Rect(); + public InsetsState() { } @@ -124,7 +132,7 @@ public class InsetsState implements Parcelable { public WindowInsets calculateInsets(Rect frame, boolean isScreenRound, boolean alwaysConsumeNavBar, DisplayCutout cutout, @Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets, - @Nullable @InsetSide SparseIntArray typeSideMap) { + int legacySoftInputMode, @Nullable @InsetSide SparseIntArray typeSideMap) { Insets[] typeInsetsMap = new Insets[Type.SIZE]; Insets[] typeMaxInsetsMap = new Insets[Type.SIZE]; boolean[] typeVisibilityMap = new boolean[SIZE]; @@ -140,8 +148,12 @@ public class InsetsState implements Parcelable { if (source == null) { continue; } - if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL - && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR)) { + + boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL + && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR); + boolean skipIme = source.getType() == TYPE_IME + && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0; + if (skipSystemBars || skipIme) { typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible(); continue; } @@ -209,6 +221,14 @@ public class InsetsState implements Parcelable { return mSources.computeIfAbsent(type, InsetsSource::new); } + public void setDisplayFrame(Rect frame) { + mDisplayFrame.set(frame); + } + + public Rect getDisplayFrame() { + return mDisplayFrame; + } + /** * Modifies the state of this class to exclude a certain type to make it ready for dispatching * to the client. @@ -224,6 +244,7 @@ public class InsetsState implements Parcelable { } public void set(InsetsState other, boolean copySources) { + mDisplayFrame.set(other.mDisplayFrame); mSources.clear(); if (copySources) { for (int i = 0; i < other.mSources.size(); i++) { @@ -323,6 +344,9 @@ public class InsetsState implements Parcelable { InsetsState state = (InsetsState) o; + if (!mDisplayFrame.equals(state.mDisplayFrame)) { + return false; + } if (mSources.size() != state.mSources.size()) { return false; } @@ -341,7 +365,7 @@ public class InsetsState implements Parcelable { @Override public int hashCode() { - return mSources.hashCode(); + return Objects.hash(mDisplayFrame, mSources); } public InsetsState(Parcel in) { @@ -355,9 +379,10 @@ public class InsetsState implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mDisplayFrame, flags); dest.writeInt(mSources.size()); for (int i = 0; i < mSources.size(); i++) { - dest.writeParcelable(mSources.valueAt(i), 0 /* flags */); + dest.writeParcelable(mSources.valueAt(i), flags); } } @@ -374,6 +399,7 @@ public class InsetsState implements Parcelable { public void readFromParcel(Parcel in) { mSources.clear(); + mDisplayFrame.set(in.readParcelable(null /* loader */)); final int size = in.readInt(); for (int i = 0; i < size; i++) { final InsetsSource source = in.readParcelable(null /* loader */); diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index c1302503134e..3f18d8b46e2a 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -33,8 +33,6 @@ import android.os.Handler; import android.os.Message; import android.os.SystemProperties; import android.os.Trace; -import android.provider.DeviceConfig; -import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; @@ -53,6 +51,7 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; +import java.util.Objects; /** * Instantiates a layout XML file into its corresponding {@link android.view.View} @@ -81,6 +80,8 @@ public abstract class LayoutInflater { private static final String TAG = LayoutInflater.class.getSimpleName(); private static final boolean DEBUG = false; + private static final String USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY + = "view.precompiled_layout_enabled"; private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex"; /** Empty stack trace used to avoid log spam in re-throw exceptions. */ @@ -111,7 +112,12 @@ public abstract class LayoutInflater { // The classloader includes the generated compiled_view.dex file. private ClassLoader mPrecompiledClassLoader; - @UnsupportedAppUsage + /** + * This is not a public API. Two APIs are now available to alleviate the need to access + * this directly: {@link #createView(Context, String, String, AttributeSet)} and + * {@link #onCreateView(Context, View, String, AttributeSet)}. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) final Object[] mConstructorArgs = new Object[2]; @UnsupportedAppUsage @@ -401,19 +407,8 @@ public abstract class LayoutInflater { } private void initPrecompiledViews() { - // Use the device config if enabled, otherwise default to the system property. - String usePrecompiledLayout = DeviceConfig.getProperty( - DeviceConfig.Runtime.NAMESPACE, - DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT); - boolean enabled = false; - if (TextUtils.isEmpty(usePrecompiledLayout)) { - enabled = SystemProperties.getBoolean( - DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT, - false); - } else { - enabled = Boolean.parseBoolean(usePrecompiledLayout); - } - initPrecompiledViews(enabled); + initPrecompiledViews( + SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false)); } private void initPrecompiledViews(boolean enablePrecompiledViews) { @@ -733,6 +728,32 @@ public abstract class LayoutInflater { } while (cl != null); return false; } + /** + * Low-level function for instantiating a view by name. This attempts to + * instantiate a view class of the given <var>name</var> found in this + * LayoutInflater's ClassLoader. To use an explicit Context in the View + * constructor, use {@link #createView(Context, String, String, AttributeSet)} instead. + * + * <p> + * There are two things that can happen in an error case: either the + * exception describing the error will be thrown, or a null will be + * returned. You must deal with both possibilities -- the former will happen + * the first time createView() is called for a class of a particular name, + * the latter every time there-after for that class name. + * + * @param name The full name of the class to be instantiated. + * @param attrs The XML attributes supplied for this instance. + * + * @return View The newly instantiated view, or null. + */ + public final View createView(String name, String prefix, AttributeSet attrs) + throws ClassNotFoundException, InflateException { + Context context = (Context) mConstructorArgs[0]; + if (context == null) { + context = mContext; + } + return createView(context, name, prefix, attrs); + } /** * Low-level function for instantiating a view by name. This attempts to @@ -746,13 +767,18 @@ public abstract class LayoutInflater { * the first time createView() is called for a class of a particular name, * the latter every time there-after for that class name. * + * @param viewContext The context used as the context parameter of the View constructor * @param name The full name of the class to be instantiated. * @param attrs The XML attributes supplied for this instance. * * @return View The newly instantiated view, or null. */ - public final View createView(String name, String prefix, AttributeSet attrs) + @Nullable + public final View createView(@NonNull Context viewContext, @NonNull String name, + @Nullable String prefix, @Nullable AttributeSet attrs) throws ClassNotFoundException, InflateException { + Objects.requireNonNull(viewContext); + Objects.requireNonNull(name); Constructor<? extends View> constructor = sConstructorMap.get(name); if (constructor != null && !verifyClassLoader(constructor)) { constructor = null; @@ -799,22 +825,21 @@ public abstract class LayoutInflater { } Object lastContext = mConstructorArgs[0]; - if (mConstructorArgs[0] == null) { - // Fill in the context if not already within inflation. - mConstructorArgs[0] = mContext; - } + mConstructorArgs[0] = viewContext; Object[] args = mConstructorArgs; args[1] = attrs; - final View view = constructor.newInstance(args); - if (view instanceof ViewStub) { - // Use the same context when inflating ViewStub later. - final ViewStub viewStub = (ViewStub) view; - viewStub.setLayoutInflater(cloneInContext((Context) args[0])); + try { + final View view = constructor.newInstance(args); + if (view instanceof ViewStub) { + // Use the same context when inflating ViewStub later. + final ViewStub viewStub = (ViewStub) view; + viewStub.setLayoutInflater(cloneInContext((Context) args[0])); + } + return view; + } finally { + mConstructorArgs[0] = lastContext; } - mConstructorArgs[0] = lastContext; - return view; - } catch (NoSuchMethodException e) { final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e); @@ -883,6 +908,26 @@ public abstract class LayoutInflater { } /** + * Version of {@link #onCreateView(View, String, AttributeSet)} that also + * takes the inflation context. The default + * implementation simply calls {@link #onCreateView(View, String, AttributeSet)}. + * + * @param viewContext The Context to be used as a constructor parameter for the View + * @param parent The future parent of the returned view. <em>Note that + * this may be null.</em> + * @param name The fully qualified class name of the View to be create. + * @param attrs An AttributeSet of attributes to apply to the View. + * + * @return View The View created. + */ + @Nullable + public View onCreateView(@NonNull Context viewContext, @Nullable View parent, + @NonNull String name, @Nullable AttributeSet attrs) + throws ClassNotFoundException { + return onCreateView(parent, name, attrs); + } + + /** * Convenience method for calling through to the five-arg createViewFromTag * method. This method passes {@code false} for the {@code ignoreThemeAttr} * argument and should be used for everything except {@code >include>} @@ -933,9 +978,9 @@ public abstract class LayoutInflater { mConstructorArgs[0] = context; try { if (-1 == name.indexOf('.')) { - view = onCreateView(parent, name, attrs); + view = onCreateView(context, parent, name, attrs); } else { - view = createView(name, null, attrs); + view = createView(context, name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index b6a4a095066f..b6c4cbbbe54d 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -24,6 +24,7 @@ import android.annotation.IntDef; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.graphics.Matrix; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -1513,7 +1514,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { } // Pointer to the native MotionEvent object that contains the actual data. - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private long mNativePtr; private MotionEvent mNext; diff --git a/core/java/android/view/RemotableViewMethod.java b/core/java/android/view/RemotableViewMethod.java index 03aed9a53925..5eff848e85aa 100644 --- a/core/java/android/view/RemotableViewMethod.java +++ b/core/java/android/view/RemotableViewMethod.java @@ -16,6 +16,8 @@ package android.view; +import android.annotation.TestApi; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -26,6 +28,7 @@ import java.lang.annotation.Target; * This annotation indicates that a method on a subclass of View * is alllowed to be used with the {@link android.widget.RemoteViews} mechanism. */ +@TestApi @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface RemotableViewMethod { diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 78ad0dabc81a..1dbc46b3e883 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -24,6 +24,7 @@ import android.graphics.CanvasProperty; import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.RenderNode; +import android.os.Build; import android.util.SparseIntArray; import com.android.internal.util.VirtualRefBasePtr; @@ -282,7 +283,7 @@ public class RenderNodeAnimator extends Animator { throw new UnsupportedOperationException(); } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) public void setTarget(View view) { mViewTarget = view; setTarget(mViewTarget.mRenderNode); diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index dc11d3ddd1d6..c24b8b2a5ee3 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -18,7 +18,6 @@ package android.view; import android.annotation.UnsupportedAppUsage; import android.content.Context; -import android.content.res.Resources; import android.os.Build; import android.os.Handler; @@ -145,7 +144,7 @@ public class ScaleGestureDetector { private boolean mInProgress; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768938) private int mSpanSlop; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768938) private int mMinSpan; private final Handler mHandler; @@ -200,10 +199,9 @@ public class ScaleGestureDetector { Handler handler) { mContext = context; mListener = listener; - mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; - - final Resources res = context.getResources(); - mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan); + final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); + mSpanSlop = viewConfiguration.getScaledTouchSlop() * 2; + mMinSpan = viewConfiguration.getScaledMinScalingSpan(); mHandler = handler; // Quick scale is enabled by default after JB_MR2 final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 8061cc3bab74..1212df0c397a 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -44,6 +44,7 @@ import android.graphics.Region; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; +import android.os.Build; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -61,6 +62,7 @@ import libcore.util.NativeAllocationRegistry; import java.io.Closeable; import java.nio.ByteBuffer; +import java.nio.ByteOrder; /** * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is @@ -131,7 +133,8 @@ public final class SurfaceControl implements Parcelable { private static native boolean nativeClearAnimationFrameStats(); private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats); - private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId); + private static native long[] nativeGetPhysicalDisplayIds(); + private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId); private static native IBinder nativeCreateDisplay(String name, boolean secure); private static native void nativeDestroyDisplay(IBinder displayToken); private static native void nativeSetDisplaySurface(long transactionObj, @@ -329,24 +332,6 @@ public final class SurfaceControl implements Parcelable { */ private static final int SURFACE_OPAQUE = 0x02; - - /* built-in physical display ids (keep in sync with ISurfaceComposer.h) - * these are different from the logical display ids used elsewhere in the framework */ - - /** - * Built-in physical display id: Main display. - * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}. - * @hide - */ - public static final int BUILT_IN_DISPLAY_ID_MAIN = 0; - - /** - * Built-in physical display id: Attached HDMI display. - * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}. - * @hide - */ - public static final int BUILT_IN_DISPLAY_ID_HDMI = 1; - // Display power modes. /** * Display power mode off: used while blanking the screen. @@ -727,8 +712,10 @@ public final class SurfaceControl implements Parcelable { for (int i = 0; i < metadata.size(); ++i) { metaParcel.writeInt(metadata.keyAt(i)); metaParcel.writeByteArray( - ByteBuffer.allocate(4).putInt(metadata.valueAt(i)).array()); + ByteBuffer.allocate(4).order(ByteOrder.nativeOrder()) + .putInt(metadata.valueAt(i)).array()); } + metaParcel.setDataPosition(0); } mNativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0, metaParcel); @@ -868,12 +855,11 @@ public final class SurfaceControl implements Parcelable { } /** - * Free all server-side state associated with this surface and - * release this object's reference. This method can only be - * called from the process that created the service. + * Release the local resources like {@link #release} but also + * remove the Surface from the screen. * @hide */ - public void destroy() { + public void remove() { if (mNativeObject != 0) { nativeDestroy(mNativeObject); mNativeObject = 0; @@ -1729,9 +1715,28 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - @UnsupportedAppUsage - public static IBinder getBuiltInDisplay(int builtInDisplayId) { - return nativeGetBuiltInDisplay(builtInDisplayId); + public static long[] getPhysicalDisplayIds() { + return nativeGetPhysicalDisplayIds(); + } + + /** + * @hide + */ + public static IBinder getPhysicalDisplayToken(long physicalDisplayId) { + return nativeGetPhysicalDisplayToken(physicalDisplayId); + } + + /** + * TODO(116025192): Remove this stopgap once framework is display-agnostic. + * + * @hide + */ + public static IBinder getInternalDisplayToken() { + final long[] physicalDisplayIds = getPhysicalDisplayIds(); + if (physicalDisplayIds.length == 0) { + return null; + } + return getPhysicalDisplayToken(physicalDisplayIds[0]); } /** @@ -1790,8 +1795,12 @@ public final class SurfaceControl implements Parcelable { public static Bitmap screenshot(Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation) { // TODO: should take the display as a parameter - IBinder displayToken = SurfaceControl.getBuiltInDisplay( - SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); + final IBinder displayToken = SurfaceControl.getInternalDisplayToken(); + if (displayToken == null) { + Log.w(TAG, "Failed to take screenshot because internal display is disconnected"); + return null; + } + if (rotation == ROTATION_90 || rotation == ROTATION_270) { rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90; } @@ -2194,7 +2203,7 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) public Transaction setLayerStack(SurfaceControl sc, int layerStack) { sc.checkNotReleased(); nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack); @@ -2460,5 +2469,23 @@ public final class SurfaceControl implements Parcelable { nativeMergeTransaction(mNativeObject, other.mNativeObject); return this; } + + /** + * Equivalent to reparent with a null parent, in that it removes + * the SurfaceControl from the scene, but it also releases + * the local resources (by calling {@link SurfaceControl#release}) + * after this method returns, {@link SurfaceControl#isValid} will return + * false for the argument. + * + * @param sc The surface to remove and release. + * @return This transaction + * @hide + */ + @NonNull + public Transaction remove(@NonNull SurfaceControl sc) { + reparent(sc, null); + sc.release(); + return this; + } } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index cd5207c50d1d..9f0800f11721 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -334,7 +334,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb updateSurface(); if (mSurfaceControl != null) { - mSurfaceControl.destroy(); + mSurfaceControl.remove(); } mSurfaceControl = null; @@ -502,11 +502,11 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb private void releaseSurfaces() { if (mSurfaceControl != null) { - mSurfaceControl.destroy(); + mSurfaceControl.remove(); mSurfaceControl = null; } if (mBackgroundControl != null) { - mBackgroundControl.destroy(); + mBackgroundControl.remove(); mBackgroundControl = null; } } @@ -816,7 +816,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } if (mDeferredDestroySurfaceControl != null) { - mDeferredDestroySurfaceControl.destroy(); + mDeferredDestroySurfaceControl.remove(); mDeferredDestroySurfaceControl = null; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 90110e60f0fd..3f2795ba1e00 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -35,6 +35,7 @@ import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; +import android.annotation.StyleRes; import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UnsupportedAppUsage; @@ -91,6 +92,7 @@ import android.util.LongSparseLongArray; import android.util.Pools.SynchronizedPool; import android.util.Property; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.StateSet; import android.util.SuperNotCalledException; import android.util.TypedValue; @@ -103,6 +105,7 @@ import android.view.WindowInsetsAnimationListener.InsetsAnimation; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeIdManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; @@ -820,7 +823,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - public static boolean mDebugViewAttributes = false; + public static boolean sDebugViewAttributes = false; + + /** + * When set to this application package view will save its attribute data. + * + * @hide + */ + public static String sDebugViewAttributesApplicationPackage; /** * Used to mark a View that has no ID. @@ -4258,47 +4268,51 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * The offset, in pixels, by which the content of this view is scrolled * horizontally. + * Please use {@link View#getScrollX()} and {@link View#setScrollX(int)} instead of + * accessing these directly. * {@hide} */ @ViewDebug.ExportedProperty(category = "scrolling") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected int mScrollX; /** * The offset, in pixels, by which the content of this view is scrolled * vertically. + * Please use {@link View#getScrollY()} and {@link View#setScrollY(int)} instead of + * accessing these directly. * {@hide} */ @ViewDebug.ExportedProperty(category = "scrolling") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected int mScrollY; /** - * The left padding in pixels, that is the distance in pixels between the - * left edge of this view and the left edge of its content. + * The final computed left padding in pixels that is used for drawing. This is the distance in + * pixels between the left edge of this view and the left edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category = "padding") @UnsupportedAppUsage protected int mPaddingLeft = 0; /** - * The right padding in pixels, that is the distance in pixels between the - * right edge of this view and the right edge of its content. + * The final computed right padding in pixels that is used for drawing. This is the distance in + * pixels between the right edge of this view and the right edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category = "padding") @UnsupportedAppUsage protected int mPaddingRight = 0; /** - * The top padding in pixels, that is the distance in pixels between the - * top edge of this view and the top edge of its content. + * The final computed top padding in pixels that is used for drawing. This is the distance in + * pixels between the top edge of this view and the top edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category = "padding") @UnsupportedAppUsage protected int mPaddingTop; /** - * The bottom padding in pixels, that is the distance in pixels between the - * bottom edge of this view and the bottom edge of its content. + * The final computed bottom padding in pixels that is used for drawing. This is the distance in + * pixels between the bottom edge of this view and the bottom edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category = "padding") @@ -4350,7 +4364,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private MatchIdPredicate mMatchIdPredicate; /** - * Cache the paddingRight set by the user to append to the scrollbar's size. + * The right padding after RTL resolution, but before taking account of scroll bars. * * @hide */ @@ -4358,7 +4372,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected int mUserPaddingRight; /** - * Cache the paddingBottom set by the user to append to the scrollbar's size. + * The resolved bottom padding before taking account of scroll bars. * * @hide */ @@ -4366,7 +4380,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected int mUserPaddingBottom; /** - * Cache the paddingLeft set by the user to append to the scrollbar's size. + * The left padding after RTL resolution, but before taking account of scroll bars. * * @hide */ @@ -4388,14 +4402,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mUserPaddingEnd; /** - * Cache initial left padding. + * The left padding as set by a setter method, a background's padding, or via XML property + * resolution. This value is the padding before LTR resolution or taking account of scrollbars. * * @hide */ int mUserPaddingLeftInitial; /** - * Cache initial right padding. + * The right padding as set by a setter method, a background's padding, or via XML property + * resolution. This value is the padding before LTR resolution or taking account of scrollbars. * * @hide */ @@ -4407,12 +4423,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int UNDEFINED_PADDING = Integer.MIN_VALUE; /** - * Cache if a left padding has been defined + * Cache if a left padding has been defined explicitly via padding, horizontal padding, + * or leftPadding in XML, or by setPadding(...) or setRelativePadding(...) */ private boolean mLeftPaddingDefined = false; /** - * Cache if a right padding has been defined + * Cache if a right padding has been defined explicitly via padding, horizontal padding, + * or rightPadding in XML, or by setPadding(...) or setRelativePadding(...) */ private boolean mRightPaddingDefined = false; @@ -5070,6 +5088,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @LayoutRes private int mSourceLayoutId = ID_NULL; + @Nullable + private SparseIntArray mAttributeSourceResId; + + @Nullable + private int[] mAttributeResolutionStack; + + @StyleRes + private int mExplicitStyle; + /** * Cached reference to the {@link ContentCaptureSession}, is reset on {@link #invalidate()}. */ @@ -5245,7 +5272,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes); - if (mDebugViewAttributes) { + retrieveExplicitStyle(context.getTheme(), attrs); + saveAttributeDataForStyleable(context, com.android.internal.R.styleable.View, attrs, a, + defStyleAttr, defStyleRes); + + if (sDebugViewAttributes) { saveAttributeData(attrs, a); } @@ -5321,7 +5352,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case com.android.internal.R.styleable.View_paddingVertical: paddingVertical = a.getDimensionPixelSize(attr, -1); break; - case com.android.internal.R.styleable.View_paddingLeft: + case com.android.internal.R.styleable.View_paddingLeft: leftPadding = a.getDimensionPixelSize(attr, -1); mUserPaddingLeftInitial = leftPadding; leftPaddingDefined = true; @@ -5787,7 +5818,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOverScrollMode(overScrollMode); - // Cache start/end user padding as we cannot fully resolve padding here (we dont have yet + // Cache start/end user padding as we cannot fully resolve padding here (we don't have yet // the resolved layout direction). Those cached values will be used later during padding // resolution. mUserPaddingStart = startPadding; @@ -5802,6 +5833,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLeftPaddingDefined = leftPaddingDefined; mRightPaddingDefined = rightPaddingDefined; + // Valid paddingHorizontal/paddingVertical beats leftPadding, rightPadding, topPadding, + // bottomPadding, and padding set by background. Valid padding beats everything. if (padding >= 0) { leftPadding = padding; topPadding = padding; @@ -5854,6 +5887,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + // mPaddingTop and mPaddingBottom may have been set by setBackground(Drawable) so must pass + // them on if topPadding or bottomPadding are not valid. internalSetPadding( mUserPaddingLeftInitial, topPadding >= 0 ? topPadding : mPaddingTop, @@ -5903,6 +5938,84 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Returns the ordered list of resource ID that are considered when resolving attribute values + * for this {@link View}. The list will include layout resource ID if the View is inflated from + * XML. It will also include a set of explicit styles if specified in XML using + * {@code style="..."}. Finally, it will include the default styles resolved from the theme. + * + * <p> + * <b>Note:</b> this method will only return actual values if the view attribute debugging + * is enabled in Android developer options. + * + * @return ordered list of resource ID that are considered when resolving attribute values for + * this {@link View}. + */ + @NonNull + public List<Integer> getAttributeResolutionStack() { + ArrayList<Integer> stack = new ArrayList<>(); + if (!sDebugViewAttributes) { + return stack; + } + if (mSourceLayoutId != ID_NULL) { + stack.add(mSourceLayoutId); + } + for (int i = 0; i < mAttributeResolutionStack.length; i++) { + stack.add(mAttributeResolutionStack[i]); + } + return stack; + } + + /** + * Returns the mapping of attribute resource ID to source resource ID where the attribute value + * was set. Source resource ID can either be a layout resource ID, if the value was set in XML + * within the View tag, or a style resource ID, if the attribute was set in a style. The source + * resource value will be one of the resource IDs from {@link #getAttributeSourceResourceMap()}. + * + * <p> + * <b>Note:</b> this method will only return actual values if the view attribute debugging + * is enabled in Android developer options. + * + * @return mapping of attribute resource ID to source resource ID where the attribute value + * was set. + */ + @NonNull + public Map<Integer, Integer> getAttributeSourceResourceMap() { + HashMap<Integer, Integer> map = new HashMap<>(); + if (!sDebugViewAttributes) { + return map; + } + for (int i = 0; i < mAttributeSourceResId.size(); i++) { + map.put(mAttributeSourceResId.keyAt(i), mAttributeSourceResId.valueAt(i)); + } + return map; + } + + /** + * Returns the resource ID for the style specified using {@code style="..."} in the + * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise if not + * specified or otherwise not applicable. + * <p> + * Each {@link View} can have an explicit style specified in the layout file. + * This style is used first during the {@link View} attribute resolution, then if an attribute + * is not defined there the resource system looks at default style and theme as fallbacks. + * + * <p> + * <b>Note:</b> this method will only return actual values if the view attribute debugging + * is enabled in Android developer options. + * + * @return The resource ID for the style specified using {@code style="..."} in the + * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise + * if not specified or otherwise not applicable. + */ + @StyleRes + public int getExplicitStyle() { + if (!sDebugViewAttributes) { + return ID_NULL; + } + return mExplicitStyle; + } + + /** * An implementation of OnClickListener that attempts to lazily load a * named click handling method from a parent or ancestor context. */ @@ -5988,6 +6101,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mAttributeMap; } + private void retrieveExplicitStyle(@NonNull Resources.Theme theme, + @Nullable AttributeSet attrs) { + if (!sDebugViewAttributes) { + return; + } + mExplicitStyle = theme.getExplicitStyle(attrs); + } + + /** + * Stores debugging information about attributes. This should be called in a constructor by + * every custom {@link View} that uses a custom styleable. + * @param context Context under which this view is created. + * @param styleable A reference to styleable array R.styleable.Foo + * @param attrs AttributeSet used to construct this view. + * @param t Resolved {@link TypedArray} returned by a call to + * {@link Resources#obtainAttributes(AttributeSet, int[])}. + * @param defStyleAttr Default style attribute passed into the view constructor. + * @param defStyleRes Default style resource passed into the view constructor. + */ + public final void saveAttributeDataForStyleable(@NonNull Context context, + @NonNull int[] styleable, @Nullable AttributeSet attrs, @NonNull TypedArray t, + int defStyleAttr, int defStyleRes) { + if (!sDebugViewAttributes) { + return; + } + + mAttributeResolutionStack = context.getTheme().getAttributeResolutionStack( + defStyleAttr, defStyleRes, mExplicitStyle); + + if (mAttributeSourceResId == null) { + mAttributeSourceResId = new SparseIntArray(); + } + + final int indexCount = t.getIndexCount(); + for (int j = 0; j < indexCount; ++j) { + final int index = t.getIndex(j); + mAttributeSourceResId.append(styleable[index], t.getSourceResourceId(index, 0)); + } + } + private void saveAttributeData(@Nullable AttributeSet attrs, @NonNull TypedArray t) { final int attrsCount = attrs == null ? 0 : attrs.getAttributeCount(); final int indexCount = t.getIndexCount(); @@ -16183,7 +16336,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if the View subclass handles alpha (the return value for onSetAlpha()) and * the new value for the alpha property is different from the old value */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768435) boolean setAlphaNoInvalidation(float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { @@ -19029,6 +19182,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, jumpDrawablesToCurrentState(); + AccessibilityNodeIdManager.getInstance().registerViewWithId(this, getAccessibilityViewId()); resetSubtreeAccessibilityStateChanged(); // rebuild, since Outline not maintained while View is detached @@ -19421,6 +19575,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mViewFlags & TOOLTIP) == TOOLTIP) { hideTooltip(); } + + AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId()); } private void cleanupDraw() { @@ -23273,7 +23429,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768420) protected void internalSetPadding(int left, int top, int right, int bottom) { mUserPaddingLeft = left; mUserPaddingRight = right; @@ -23935,24 +24091,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Finds a view by its unuque and stable accessibility id. - * - * @param accessibilityId The searched accessibility id. - * @return The found view. - */ - @UnsupportedAppUsage - final <T extends View> T findViewByAccessibilityId(int accessibilityId) { - if (accessibilityId < 0) { - return null; - } - T view = findViewByAccessibilityIdTraversal(accessibilityId); - if (view != null) { - return view.includeForAccessibility() ? view : null; - } - return null; - } - - /** * Performs the traversal to find a view by its unique and stable accessibility id. * * <strong>Note:</strong>This method does not stop at the root namespace diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 6beae37d6b46..bb29ed6c5339 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -293,12 +293,14 @@ public class ViewConfiguration { */ private static final float AMBIGUOUS_GESTURE_MULTIPLIER = 2f; + private final boolean mConstructedWithContext; private final int mEdgeSlop; private final int mFadingEdgeLength; private final int mMinimumFlingVelocity; private final int mMaximumFlingVelocity; private final int mScrollbarSize; private final int mTouchSlop; + private final int mMinScalingSpan; private final int mHoverSlop; private final int mMinScrollbarTouchTarget; private final int mDoubleTapTouchSlop; @@ -329,6 +331,7 @@ public class ViewConfiguration { */ @Deprecated public ViewConfiguration() { + mConstructedWithContext = false; mEdgeSlop = EDGE_SLOP; mFadingEdgeLength = FADING_EDGE_LENGTH; mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY; @@ -350,6 +353,10 @@ public class ViewConfiguration { mHorizontalScrollFactor = HORIZONTAL_SCROLL_FACTOR; mVerticalScrollFactor = VERTICAL_SCROLL_FACTOR; mShowMenuShortcutsWhenKeyboardPresent = false; + + // Getter throws if mConstructedWithContext is false so doesn't matter what + // this value is. + mMinScalingSpan = 0; } /** @@ -363,6 +370,7 @@ public class ViewConfiguration { * @see android.util.DisplayMetrics */ private ViewConfiguration(Context context) { + mConstructedWithContext = true; final Resources res = context.getResources(); final DisplayMetrics metrics = res.getDisplayMetrics(); final Configuration config = res.getConfiguration(); @@ -447,6 +455,8 @@ public class ViewConfiguration { mShowMenuShortcutsWhenKeyboardPresent = res.getBoolean( com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent); + mMinScalingSpan = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_minScalingSpan); } /** @@ -959,6 +969,26 @@ public class ViewConfiguration { } /** + * Retrieves the distance in pixels between touches that must be reached for a gesture to be + * interpreted as scaling. + * + * In general, scaling shouldn't start until this distance has been met or surpassed, and + * scaling should end when the distance in pixels between touches drops below this distance. + * + * @return The distance in pixels + * @throws IllegalStateException if this method is called on a ViewConfiguration that was + * instantiated using a constructor with no Context parameter. + */ + public int getScaledMinScalingSpan() { + if (!mConstructedWithContext) { + throw new IllegalStateException("Min scaling span cannot be determined when this " + + "method is called on a ViewConfiguration that was instantiated using a " + + "constructor with no Context parameter"); + } + return mMinScalingSpan; + } + + /** * @hide * @return Whether or not marquee should use fading edges. */ diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index a0ab362f3985..68c0d9ec465b 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -1139,12 +1139,6 @@ public class ViewPropertyAnimator { boolean hardwareAccelerated = mView.isHardwareAccelerated(); - // alpha requires slightly different treatment than the other (transform) properties. - // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation - // logic is dependent on how the view handles an internal call to onSetAlpha(). - // We track what kinds of properties are set, and how alpha is handled when it is - // set, and perform the invalidation steps appropriately. - boolean alphaHandled = false; if (!hardwareAccelerated) { mView.invalidateParentCaches(); } @@ -1159,11 +1153,7 @@ public class ViewPropertyAnimator { for (int i = 0; i < count; ++i) { NameValuesHolder values = valueList.get(i); float value = values.mFromValue + fraction * values.mDeltaValue; - if (values.mNameConstant == ALPHA) { - alphaHandled = mView.setAlphaNoInvalidation(value); - } else { - setValue(values.mNameConstant, value); - } + setValue(values.mNameConstant, value); } } if ((propertyMask & TRANSFORM_MASK) != 0) { @@ -1171,13 +1161,8 @@ public class ViewPropertyAnimator { mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation } } - // invalidate(false) in all cases except if alphaHandled gets set to true - // via the call to setAlphaNoInvalidation(), above - if (alphaHandled) { - mView.invalidate(true); - } else { - mView.invalidateViewProperty(false, false); - } + + mView.invalidateViewProperty(false, false); if (mUpdateListener != null) { mUpdateListener.onAnimationUpdate(animation); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 47528a05f5a2..1a782ee5d3dd 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -95,6 +95,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener; +import android.view.accessibility.AccessibilityNodeIdManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; @@ -1602,7 +1603,7 @@ public final class ViewRootImpl implements ViewParent, mSurfaceSession = null; if (mBoundsSurfaceControl != null) { - mBoundsSurfaceControl.destroy(); + mBoundsSurfaceControl.remove(); mBoundsSurface.release(); mBoundsSurfaceControl = null; } @@ -1886,7 +1887,7 @@ public final class ViewRootImpl implements ViewParent, mLastWindowInsets = mInsetsController.calculateInsets( mContext.getResources().getConfiguration().isScreenRound(), mAttachInfo.mAlwaysConsumeNavBar, displayCutout, - contentInsets, stableInsets); + contentInsets, stableInsets, mWindowAttributes.softInputMode); } else { mLastWindowInsets = new WindowInsets(contentInsets, stableInsets, mContext.getResources().getConfiguration().isScreenRound(), @@ -7954,17 +7955,14 @@ public final class ViewRootImpl implements ViewParent, // Intercept accessibility focus events fired by virtual nodes to keep // track of accessibility focus position in such nodes. final int eventType = event.getEventType(); + final View source = getSourceForAccessibilityEvent(event); switch (eventType) { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { - final long sourceNodeId = event.getSourceNodeId(); - final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( - sourceNodeId); - View source = mView.findViewByAccessibilityId(accessibilityViewId); if (source != null) { AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); if (provider != null) { final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( - sourceNodeId); + event.getSourceNodeId()); final AccessibilityNodeInfo node; node = provider.createAccessibilityNodeInfo(virtualNodeId); setAccessibilityFocus(source, node); @@ -7972,15 +7970,8 @@ public final class ViewRootImpl implements ViewParent, } } break; case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { - final long sourceNodeId = event.getSourceNodeId(); - final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( - sourceNodeId); - View source = mView.findViewByAccessibilityId(accessibilityViewId); - if (source != null) { - AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); - if (provider != null) { - setAccessibilityFocus(null, null); - } + if (source != null && source.getAccessibilityNodeProvider() != null) { + setAccessibilityFocus(null, null); } } break; @@ -7993,6 +7984,13 @@ public final class ViewRootImpl implements ViewParent, return true; } + private View getSourceForAccessibilityEvent(AccessibilityEvent event) { + final long sourceNodeId = event.getSourceNodeId(); + final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( + sourceNodeId); + return AccessibilityNodeIdManager.getInstance().findView(accessibilityViewId); + } + /** * Updates the focused virtual view, when necessary, in response to a * content changed event. diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 46aea80efe7a..e3833c01eeaf 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -520,7 +520,7 @@ public final class WindowManagerGlobal { return false; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public void trimMemory(int level) { if (ThreadedRenderer.isAvailable()) { if (shouldDestroyEglContext(level)) { diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 16bafe2c4f11..35ed7bfa2ce6 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -16,6 +16,11 @@ package android.view; +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Constants for interfacing with WindowManagerService and WindowManagerPolicyInternal. * @hide @@ -89,6 +94,35 @@ public interface WindowManagerPolicyConstants { /** Screen turned off because of timeout */ int OFF_BECAUSE_OF_TIMEOUT = 3; + @IntDef(prefix = { "ON_BECAUSE_OF_" }, value = { + ON_BECAUSE_OF_USER, + ON_BECAUSE_OF_APPLICATION, + ON_BECAUSE_OF_UNKNOWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OnReason{} + + /** Convert the on reason to a human readable format */ + static String onReasonToString(@OnReason int why) { + switch (why) { + case ON_BECAUSE_OF_USER: + return "ON_BECAUSE_OF_USER"; + case ON_BECAUSE_OF_APPLICATION: + return "ON_BECAUSE_OF_APPLICATION"; + case ON_BECAUSE_OF_UNKNOWN: + return "ON_BECAUSE_OF_UNKNOWN"; + default: + return Integer.toString(why); + } + } + + /** Screen turned on because of a user-initiated action. */ + int ON_BECAUSE_OF_USER = 1; + /** Screen turned on because of an application request or event */ + int ON_BECAUSE_OF_APPLICATION = 2; + /** Screen turned on for an unknown reason */ + int ON_BECAUSE_OF_UNKNOWN = 3; + int APPLICATION_LAYER = 2; int APPLICATION_MEDIA_SUBLAYER = -2; int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 6aafa348e3f5..06207a9290d7 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -36,6 +36,7 @@ import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -183,7 +184,7 @@ public final class AccessibilityManager { boolean mIsTouchExplorationEnabled; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768939) boolean mIsHighTextContrastEnabled; AccessibilityPolicy mAccessibilityPolicy; diff --git a/core/java/android/view/accessibility/AccessibilityNodeIdManager.java b/core/java/android/view/accessibility/AccessibilityNodeIdManager.java new file mode 100644 index 000000000000..1ac704774e7f --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityNodeIdManager.java @@ -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. + */ + +package android.view.accessibility; + +import android.util.SparseArray; +import android.view.View; + +/** @hide */ +public final class AccessibilityNodeIdManager { + private SparseArray<View> mIdsToViews = new SparseArray<>(); + private static AccessibilityNodeIdManager sIdManager; + + /** + * Gets singleton. + * @return The instance. + */ + public static synchronized AccessibilityNodeIdManager getInstance() { + if (sIdManager == null) { + sIdManager = new AccessibilityNodeIdManager(); + } + return sIdManager; + } + + private AccessibilityNodeIdManager() { + } + + /** + * Register view to be kept track of by the accessibility system. + * Must be paired with unregisterView, otherwise this will leak. + * @param view The view to be registered. + * @param id The accessibilityViewId of the view. + */ + public void registerViewWithId(View view, int id) { + mIdsToViews.append(id, view); + } + + /** + * Unregister view, accessibility won't keep track of this view after this call. + * @param id The id returned from registerView when the view as first associated. + */ + public void unregisterViewWithId(int id) { + mIdsToViews.remove(id); + } + + /** + * Accessibility uses this to find the view in the hierarchy. + * @param id The accessibility view id. + * @return The view. + */ + public View findView(int id) { + final View view = mIdsToViews.get(id); + return view != null && view.includeForAccessibility() ? view : null; + } +} diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index dfd9a2e95cb7..e0950948afb8 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -23,6 +23,7 @@ import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.graphics.RectF; +import android.os.Build; import android.os.Handler; import android.os.SystemProperties; import android.util.AttributeSet; @@ -30,9 +31,6 @@ import android.util.TypedValue; import dalvik.system.CloseGuard; -import java.util.ArrayList; -import java.util.List; - /** * Abstraction for an Animation that can be applied to Views, Surfaces, or * other objects. See the {@link android.view.animation animation package @@ -187,15 +185,12 @@ public abstract class Animation implements Cloneable { /** * An animation listener to be notified when the animation starts, ends or repeats. */ - @UnsupportedAppUsage + // If you need to chain the AnimationListener, wrap the existing Animation into an AnimationSet + // and add your new listener to that set + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117519981) private AnimationListener mListener; /** - * A list of animation listeners to be notified when the animation starts, ends or repeats. - */ - private List<AnimationListener> mListeners; - - /** * Desired Z order mode during animation. */ private int mZAdjustment; @@ -833,7 +828,7 @@ public abstract class Animation implements Cloneable { } private boolean hasAnimationListener() { - return mListener != null || (mListeners != null && !mListeners.isEmpty()); + return mListener != null; } /** @@ -848,32 +843,6 @@ public abstract class Animation implements Cloneable { } /** - * <p>Adds an animation listener to this animation. The animation listener - * is notified of animation events such as the end of the animation or the - * repetition of the animation.</p> - * - * @param listener the animation listener to be notified - */ - public void addAnimationListener(AnimationListener listener) { - if (mListeners == null) { - mListeners = new ArrayList<>(1); - } - mListeners.add(listener); - } - - /** - * <p>Removes an animation listener that has been added with - * {@link #addAnimationListener(AnimationListener)}.</p> - * - * @param listener the animation listener to be removed - */ - public void removeAnimationListener(AnimationListener listener) { - if (mListeners != null) { - mListeners.remove(listener); - } - } - - /** * Gurantees that this animation has an interpolator. Will use * a AccelerateDecelerateInterpolator is nothing else was specified. */ @@ -1003,33 +972,18 @@ public abstract class Animation implements Cloneable { if (mListener != null) { mListener.onAnimationStart(this); } - if (mListeners != null && !mListeners.isEmpty()) { - for (AnimationListener listener : mListeners) { - listener.onAnimationStart(this); - } - } } void dispatchAnimationRepeat() { if (mListener != null) { mListener.onAnimationRepeat(this); } - if (mListeners != null && !mListeners.isEmpty()) { - for (AnimationListener listener : mListeners) { - listener.onAnimationRepeat(this); - } - } } void dispatchAnimationEnd() { if (mListener != null) { mListener.onAnimationEnd(this); } - if (mListeners != null && !mListeners.isEmpty()) { - for (AnimationListener listener : mListeners) { - listener.onAnimationEnd(this); - } - } } /** diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index f1c7b695ce05..5608bb372701 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -52,11 +52,13 @@ public final class AutofillId implements Parcelable { } /** @hide */ + @TestApi public AutofillId(int parentId, int virtualChildId) { this(FLAG_IS_VIRTUAL_INT, parentId, virtualChildId, NO_SESSION); } /** @hide */ + @TestApi public AutofillId(@NonNull AutofillId parent, long virtualChildId, int sessionId) { this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId); } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 1c96b87394b6..83fc017373a6 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -346,9 +346,6 @@ public final class AutofillManager { @TestApi public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1; - /** @hide */ // TODO(b/123233342): remove when not used anymore - public static final int FLAG_SMART_SUGGESTION_LEGACY = 0x2; - /** @hide */ @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = { FLAG_SMART_SUGGESTION_SYSTEM diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index dfac35ddf58b..22254cd94059 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -138,8 +138,11 @@ public final class ContentCaptureEvent implements Parcelable { /** * Adds an autofill id to the this event, merging the single id into a list if necessary. - * @hide */ + * + * @hide + */ public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) { + Preconditions.checkNotNull(id); if (mIds == null) { mIds = new ArrayList<>(); if (mId == null) { diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 2512b95fe0ec..634443d78b49 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -66,6 +66,25 @@ public final class ContentCaptureManager { */ private static final int SYNC_CALLS_TIMEOUT_MS = 5000; + /** + * DeviceConfig property used by {@code com.android.server.SystemServer} on start to decide + * whether the Content Capture service should be created or not + * + * <p>By default it should *NOT* be set (or set to {@code "default"}, so the decision is based + * on whether the OEM provides an implementation for the service), but it can be overridden to: + * + * <ul> + * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency (when + * it's set to {@code "false"}). + * <li>Enable the CTS tests to be run on AOSP builds (when it's set to {@code "true"}). + * </ul> + * + * @hide + */ + @TestApi + public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = + "service_explicitly_enabled"; + private final Object mLock = new Object(); @NonNull diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index eb945b55bd7e..810c967ce2c8 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -303,6 +303,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text=" + getSanitizedString(event.getText())); } + // TODO(b/124107816): should call lastEvent.merge(event) instead lastEvent.setText(event.getText()); addEvent = false; } @@ -316,7 +317,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session " + lastEvent.getSessionId()); } - lastEvent.addAutofillId(event.getId()); + mergeViewsDisappearedEvent(lastEvent, event); addEvent = false; } } @@ -364,6 +365,30 @@ public final class MainContentCaptureSession extends ContentCaptureSession { flush(flushReason); } + // TODO(b/124107816): should be ContentCaptureEvent Event.merge(event) instead (which would + // replace the addAutofillId() method - we would also need unit tests on ContentCaptureEventTest + // to check these scenarios) + private void mergeViewsDisappearedEvent(@NonNull ContentCaptureEvent lastEvent, + @NonNull ContentCaptureEvent event) { + final List<AutofillId> ids = event.getIds(); + final AutofillId id = event.getId(); + if (ids != null) { + if (id != null) { + Log.w(TAG, "got TYPE_VIEW_DISAPPEARED event with both id and ids: " + event); + } + for (int i = 0; i < ids.size(); i++) { + lastEvent.addAutofillId(ids.get(i)); + } + return; + } + if (id != null) { + lastEvent.addAutofillId(id); + return; + } + throw new IllegalArgumentException( + "got TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event); + } + @UiThread private boolean hasStarted() { return mState != UNKNOWN_STATE; diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index eef841da3bc5..8d6245458d62 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -34,7 +34,6 @@ import android.view.ViewStructure.HtmlInfo.Builder; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; //TODO(b/122484602): add javadocs / implement Parcelable / implement @@ -589,6 +588,7 @@ public final class ViewNode extends AssistStructure.ViewNode { } /** @hide */ + @TestApi public static void writeToParcel(@NonNull Parcel parcel, @Nullable ViewNode node, int flags) { if (node == null) { parcel.writeLong(0); @@ -598,18 +598,20 @@ public final class ViewNode extends AssistStructure.ViewNode { } /** @hide */ + @TestApi public static @Nullable ViewNode readFromParcel(@NonNull Parcel parcel) { final long nodeFlags = parcel.readLong(); - return nodeFlags == 0 ? new ViewNode() : new ViewNode(nodeFlags, parcel); + return nodeFlags == 0 ? null : new ViewNode(nodeFlags, parcel); } /** @hide */ - @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk. + @TestApi public static final class ViewStructureImpl extends ViewStructure { final ViewNode mNode = new ViewNode(); - @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk. + /** @hide */ + @TestApi public ViewStructureImpl(@NonNull View view) { mNode.mAutofillId = Preconditions.checkNotNull(view).getAutofillId(); final ViewParent parent = view.getParent(); @@ -618,13 +620,15 @@ public final class ViewNode extends AssistStructure.ViewNode { } } - @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk. + /** @hide */ + @TestApi public ViewStructureImpl(@NonNull AutofillId parentId, long virtualId, int sessionId) { mNode.mParentAutofillId = Preconditions.checkNotNull(parentId); mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId); } - @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk. + /** @hide */ + @TestApi public ViewNode getNode() { return mNode; } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index aee4b1f63812..8a097883dd5c 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -339,7 +339,11 @@ public final class InputMethodManager { // For scheduling work on the main thread. This also serves as our // global lock. - @UnsupportedAppUsage + // Remark on @UnsupportedAppUsage: there were context leaks on old versions + // of android (b/37043700), so developers used this field to perform manual clean up. + // Leaks were fixed, hacks were backported to AppCompatActivity, + // so an access to the field is closed. + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) final H mH; // Our generic input connection if the current target does not have its own. @@ -375,13 +379,15 @@ public final class InputMethodManager { * This is the view that should currently be served by an input method, * regardless of the state of setting that up. */ - @UnsupportedAppUsage + // See comment to mH field in regard to @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) View mServedView; /** * This is then next view that will be served by the input method, when * we get around to updating things. */ - @UnsupportedAppUsage + // See comment to mH field in regard to @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) View mNextServedView; /** * This is set when we are in the process of connecting, to determine diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java index 1a6e5d8e8b03..ae6a645c90a1 100644 --- a/core/java/android/view/textclassifier/ConversationAction.java +++ b/core/java/android/view/textclassifier/ConversationAction.java @@ -92,6 +92,9 @@ public final class ConversationAction implements Parcelable { */ public static final String TYPE_SHARE_LOCATION = "share_location"; + /** @hide **/ + public static final String TYPE_ADD_CONTACT = "add_contact"; + public static final Creator<ConversationAction> CREATOR = new Creator<ConversationAction>() { @Override diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java index 602455c65beb..b0e7ad5d7264 100644 --- a/core/java/android/view/textclassifier/ExtrasUtils.java +++ b/core/java/android/view/textclassifier/ExtrasUtils.java @@ -85,15 +85,16 @@ public final class ExtrasUtils { } /** - * Returns the first "translate" action found in the {@code classification} object. + * Returns the first action found in the {@code classification} object with an intent + * action string, {@code intentAction}. */ @Nullable - public static RemoteAction findTranslateAction(TextClassification classification) { + public static RemoteAction findAction(TextClassification classification, String intentAction) { final ArrayList<Intent> actionIntents = getActionsIntents(classification); if (actionIntents != null) { final int size = actionIntents.size(); for (int i = 0; i < size; i++) { - if (Intent.ACTION_TRANSLATE.equals(actionIntents.get(i).getAction())) { + if (intentAction.equals(actionIntents.get(i).getAction())) { return classification.getActions().get(i); } } @@ -102,6 +103,14 @@ public final class ExtrasUtils { } /** + * Returns the first "translate" action found in the {@code classification} object. + */ + @Nullable + public static RemoteAction findTranslateAction(TextClassification classification) { + return findAction(classification, Intent.ACTION_TRANSLATE); + } + + /** * Returns the entity type contained in the {@code extra}. */ @Nullable diff --git a/core/java/android/view/textclassifier/LegacyIntentFactory.java b/core/java/android/view/textclassifier/LegacyIntentFactory.java index b6e5b3e26b16..2d0d032cfef3 100644 --- a/core/java/android/view/textclassifier/LegacyIntentFactory.java +++ b/core/java/android/view/textclassifier/LegacyIntentFactory.java @@ -182,7 +182,8 @@ public final class LegacyIntentFactory implements IntentFactory { actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.browse), context.getString(com.android.internal.R.string.browse_desc), - new Intent(Intent.ACTION_VIEW, Uri.parse(text)) + new Intent(Intent.ACTION_VIEW) + .setDataAndNormalize(Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()), LabeledIntent.DEFAULT_REQUEST_CODE)); return actions; diff --git a/core/java/android/view/textclassifier/TemplateIntentFactory.java b/core/java/android/view/textclassifier/TemplateIntentFactory.java index 52788436b3ea..95f88c7de146 100644 --- a/core/java/android/view/textclassifier/TemplateIntentFactory.java +++ b/core/java/android/view/textclassifier/TemplateIntentFactory.java @@ -49,20 +49,18 @@ public final class TemplateIntentFactory { } final List<TextClassifierImpl.LabeledIntent> labeledIntents = new ArrayList<>(); for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) { - Intent intent = createIntent(remoteActionTemplate); - if (intent == null) { + if (!isValidTemplate(remoteActionTemplate)) { + Log.w(TAG, "Invalid RemoteActionTemplate skipped."); continue; } - TextClassifierImpl.LabeledIntent - labeledIntent = new TextClassifierImpl.LabeledIntent( - remoteActionTemplate.title, - remoteActionTemplate.description, - intent, - remoteActionTemplate.requestCode == null - ? TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE - : remoteActionTemplate.requestCode - ); - labeledIntents.add(labeledIntent); + labeledIntents.add( + new TextClassifierImpl.LabeledIntent( + remoteActionTemplate.title, + remoteActionTemplate.description, + createIntent(remoteActionTemplate), + remoteActionTemplate.requestCode == null + ? TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE + : remoteActionTemplate.requestCode)); } labeledIntents.forEach( action -> action.getIntent() @@ -70,29 +68,43 @@ public final class TemplateIntentFactory { return labeledIntents; } - @Nullable - private static Intent createIntent(RemoteActionTemplate remoteActionTemplate) { - Intent intent = new Intent(); - if (!TextUtils.isEmpty(remoteActionTemplate.packageName)) { - Log.w(TAG, "A RemoteActionTemplate is skipped as package name is set."); - return null; + private static boolean isValidTemplate(@Nullable RemoteActionTemplate remoteActionTemplate) { + if (remoteActionTemplate == null) { + Log.w(TAG, "Invalid RemoteActionTemplate: is null"); + return false; } - if (!TextUtils.isEmpty(remoteActionTemplate.action)) { - intent.setAction(remoteActionTemplate.action); + if (TextUtils.isEmpty(remoteActionTemplate.title)) { + Log.w(TAG, "Invalid RemoteActionTemplate: title is null"); + return false; } - Uri data = null; - if (!TextUtils.isEmpty(remoteActionTemplate.data)) { - data = Uri.parse(remoteActionTemplate.data); + if (TextUtils.isEmpty(remoteActionTemplate.description)) { + Log.w(TAG, "Invalid RemoteActionTemplate: description is null"); + return false; } - if (data != null || !TextUtils.isEmpty(remoteActionTemplate.type)) { - intent.setDataAndType(data, remoteActionTemplate.type); + if (!TextUtils.isEmpty(remoteActionTemplate.packageName)) { + Log.w(TAG, "Invalid RemoteActionTemplate: package name is set"); + return false; } - if (remoteActionTemplate.flags != null) { - intent.setFlags(remoteActionTemplate.flags); + if (TextUtils.isEmpty(remoteActionTemplate.action)) { + Log.w(TAG, "Invalid RemoteActionTemplate: intent action not set"); + return false; } + return true; + } + + private static Intent createIntent(RemoteActionTemplate remoteActionTemplate) { + final Intent intent = new Intent(remoteActionTemplate.action); + final Uri uri = TextUtils.isEmpty(remoteActionTemplate.data) + ? null : Uri.parse(remoteActionTemplate.data).normalizeScheme(); + final String type = TextUtils.isEmpty(remoteActionTemplate.type) + ? null : Intent.normalizeMimeType(remoteActionTemplate.type); + intent.setDataAndType(uri, type); + intent.setFlags(remoteActionTemplate.flags == null ? 0 : remoteActionTemplate.flags); if (remoteActionTemplate.category != null) { for (String category : remoteActionTemplate.category) { - intent.addCategory(category); + if (category != null) { + intent.addCategory(category); + } } } intent.putExtras(createExtras(remoteActionTemplate.extras)); @@ -105,6 +117,9 @@ public final class TemplateIntentFactory { } Bundle bundle = new Bundle(); for (NamedVariant namedVariant : namedVariants) { + if (namedVariant == null) { + continue; + } switch (namedVariant.getType()) { case NamedVariant.TYPE_INT: bundle.putInt(namedVariant.getName(), namedVariant.getInt()); diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index ee9e04e5329a..2ef8d04939bd 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -131,6 +131,7 @@ public final class TextClassificationConstants { .add(ConversationAction.TYPE_TRACK_FLIGHT) .add(ConversationAction.TYPE_VIEW_CALENDAR) .add(ConversationAction.TYPE_VIEW_MAP) + .add(ConversationAction.TYPE_ADD_CONTACT) .toString(); /** * < 0 : Not set. Use value from LangId model. diff --git a/core/java/android/webkit/OWNERS b/core/java/android/webkit/OWNERS index 00e540a46ab2..b33da57c42e3 100644 --- a/core/java/android/webkit/OWNERS +++ b/core/java/android/webkit/OWNERS @@ -1,3 +1,4 @@ changwan@google.com +ntfschr@google.com tobiasjs@google.com torne@google.com diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 60393502bbe7..2171fc52a0ba 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -61,6 +61,7 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.view.autofill.AutofillValue; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.inspector.InspectableProperty; import android.view.textclassifier.TextClassifier; import android.widget.AbsoluteLayout; @@ -1239,6 +1240,7 @@ public class WebView extends AbsoluteLayout * * @return the URL for the current page */ + @InspectableProperty(hasAttributeId = false) @ViewDebug.ExportedProperty(category = "webview") public String getUrl() { checkThread(); @@ -1254,6 +1256,7 @@ public class WebView extends AbsoluteLayout * * @return the URL that was originally requested for the current page */ + @InspectableProperty(hasAttributeId = false) @ViewDebug.ExportedProperty(category = "webview") public String getOriginalUrl() { checkThread(); @@ -1266,6 +1269,7 @@ public class WebView extends AbsoluteLayout * * @return the title for the current page */ + @InspectableProperty(hasAttributeId = false) @ViewDebug.ExportedProperty(category = "webview") public String getTitle() { checkThread(); @@ -1278,6 +1282,7 @@ public class WebView extends AbsoluteLayout * * @return the favicon for the current page */ + @InspectableProperty(hasAttributeId = false) public Bitmap getFavicon() { checkThread(); return mProvider.getFavicon(); @@ -1300,6 +1305,7 @@ public class WebView extends AbsoluteLayout * * @return the progress for the current page between 0 and 100 */ + @InspectableProperty(hasAttributeId = false) public int getProgress() { checkThread(); return mProvider.getProgress(); @@ -1310,6 +1316,7 @@ public class WebView extends AbsoluteLayout * * @return the height of the HTML content */ + @InspectableProperty(hasAttributeId = false) @ViewDebug.ExportedProperty(category = "webview") public int getContentHeight() { checkThread(); @@ -2276,6 +2283,11 @@ public class WebView extends AbsoluteLayout * * @return the requested renderer priority policy. */ + @InspectableProperty(hasAttributeId = false, enumMapping = { + @InspectableProperty.EnumMap(name = "waived", value = RENDERER_PRIORITY_WAIVED), + @InspectableProperty.EnumMap(name = "bound", value = RENDERER_PRIORITY_BOUND), + @InspectableProperty.EnumMap(name = "important", value = RENDERER_PRIORITY_IMPORTANT) + }) @RendererPriority public int getRendererRequestedPriority() { return mProvider.getRendererRequestedPriority(); @@ -2288,6 +2300,7 @@ public class WebView extends AbsoluteLayout * @return whether this WebView requests a priority of * {@link #RENDERER_PRIORITY_WAIVED} when not visible. */ + @InspectableProperty(hasAttributeId = false) public boolean getRendererPriorityWaivedWhenNotVisible() { return mProvider.getRendererPriorityWaivedWhenNotVisible(); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 4dd7d3a2fa71..9d7a482aa611 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -338,7 +338,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * The data set used to store unused views that should be reused during the next layout * to avoid creating new ones */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769398) final RecycleBin mRecycler = new RecycleBin(); /** @@ -421,7 +421,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * One of TOUCH_MODE_REST, TOUCH_MODE_DOWN, TOUCH_MODE_TAP, TOUCH_MODE_SCROLL, or * TOUCH_MODE_DONE_WAITING */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769413) int mTouchMode = TOUCH_MODE_REST; /** @@ -442,8 +442,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Handles one frame of a fling + * + * To interrupt a fling early you should use smoothScrollBy(0,0) instead */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private FlingRunnable mFlingRunnable; /** @@ -483,7 +485,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Optional callback to notify client when scroll position has changed */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769353) private OnScrollListener mOnScrollListener; /** @@ -632,7 +634,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Helper object that renders and controls the fast scroll thumb. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768941) private FastScroller mFastScroll; /** @@ -698,7 +700,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Maximum distance to overfling during edge effects */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769379) int mOverflingDistance; // These two EdgeGlows are always set and used together. @@ -706,15 +708,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Tracks the state of the top edge glow. + * + * Even though this field is practically final, we cannot make it final because there are apps + * setting it via reflection and they need to keep working until they target Q. */ - @UnsupportedAppUsage - private EdgeEffect mEdgeGlowTop; + @NonNull + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769408) + private EdgeEffect mEdgeGlowTop = new EdgeEffect(mContext); /** * Tracks the state of the bottom edge glow. + * + * Even though this field is practically final, we cannot make it final because there are apps + * setting it via reflection and they need to keep working until they target Q. */ - @UnsupportedAppUsage - private EdgeEffect mEdgeGlowBottom; + @NonNull + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768444) + private EdgeEffect mEdgeGlowBottom = new EdgeEffect(mContext); /** * An estimate of how many pixels are between the top of the list and @@ -921,21 +931,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mDensityScale = getContext().getResources().getDisplayMetrics().density; } - @Override - public void setOverScrollMode(int mode) { - if (mode != OVER_SCROLL_NEVER) { - if (mEdgeGlowTop == null) { - Context context = getContext(); - mEdgeGlowTop = new EdgeEffect(context); - mEdgeGlowBottom = new EdgeEffect(context); - } - } else { - mEdgeGlowTop = null; - mEdgeGlowBottom = null; - } - super.setOverScrollMode(mode); - } - /** * {@inheritDoc} */ @@ -1611,15 +1606,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return false; } - /** @hide */ - @Override - public View findViewByAccessibilityIdTraversal(int accessibilityId) { - if (accessibilityId == getAccessibilityViewId()) { - return this; - } - return super.findViewByAccessibilityIdTraversal(accessibilityId); - } - /** * Indicates whether the children's drawing cache is used during a scroll. * By default, the drawing cache is enabled but this will consume more memory. @@ -3779,7 +3765,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private void invalidateTopGlow() { - if (mEdgeGlowTop == null) { + if (!shouldDisplayEdgeEffects()) { return; } final boolean clipToPadding = getClipToPadding(); @@ -3790,7 +3776,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private void invalidateBottomGlow() { - if (mEdgeGlowBottom == null) { + if (!shouldDisplayEdgeEffects()) { return; } final boolean clipToPadding = getClipToPadding(); @@ -4215,7 +4201,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setPressed(false); - if (mEdgeGlowTop != null) { + if (shouldDisplayEdgeEffects()) { mEdgeGlowTop.onRelease(); mEdgeGlowBottom.onRelease(); } @@ -4240,6 +4226,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + private boolean shouldDisplayEdgeEffects() { + return getOverScrollMode() != OVER_SCROLL_NEVER; + } + private void onTouchCancel() { switch (mTouchMode) { case TOUCH_MODE_OVERSCROLL: @@ -4265,7 +4255,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te recycleVelocityTracker(); } - if (mEdgeGlowTop != null) { + if (shouldDisplayEdgeEffects()) { mEdgeGlowTop.onRelease(); mEdgeGlowBottom.onRelease(); } @@ -4386,7 +4376,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public void draw(Canvas canvas) { super.draw(canvas); - if (mEdgeGlowTop != null) { + if (shouldDisplayEdgeEffects()) { final int scrollY = mScrollY; final boolean clipToPadding = getClipToPadding(); final int width; @@ -4623,7 +4613,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param newState The new scroll state. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769710) void reportScrollStateChange(int newState) { if (newState != mLastScrollState) { if (mOnScrollListener != null) { @@ -4688,7 +4678,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mScroller = new OverScroller(getContext()); } - @UnsupportedAppUsage + // Use AbsListView#fling(int) instead + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void start(int initialVelocity) { int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0; mLastFlingY = initialY; @@ -4766,7 +4757,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te postOnAnimation(this); } - @UnsupportedAppUsage + // To interrupt a fling early you should use smoothScrollBy(0,0) instead + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void endFling() { mTouchMode = TOUCH_MODE_REST; @@ -5164,7 +5156,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @param incrementalDeltaY Change in deltaY from the previous event. * @return true if we're already at the beginning/end of the list and have nothing to do. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051739) boolean trackMotionScroll(int deltaY, int incrementalDeltaY) { final int childCount = getChildCount(); if (childCount == 0) { @@ -6376,7 +6368,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private void finishGlows() { - if (mEdgeGlowTop != null) { + if (shouldDisplayEdgeEffects()) { mEdgeGlowTop.finish(); mEdgeGlowBottom.finish(); } @@ -6483,6 +6475,76 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * Sets the edge effect color for both top and bottom edge effects. + * + * @param color The color for the edge effects. + * @see #setTopEdgeEffectColor(int) + * @see #setBottomEdgeEffectColor(int) + * @see #getTopEdgeEffectColor() + * @see #getBottomEdgeEffectColor() + */ + public void setEdgeEffectColor(@ColorInt int color) { + setTopEdgeEffectColor(color); + setBottomEdgeEffectColor(color); + } + + /** + * Sets the bottom edge effect color. + * + * @param color The color for the bottom edge effect. + * @see #setTopEdgeEffectColor(int) + * @see #setEdgeEffectColor(int) + * @see #getTopEdgeEffectColor() + * @see #getBottomEdgeEffectColor() + */ + public void setBottomEdgeEffectColor(@ColorInt int color) { + mEdgeGlowBottom.setColor(color); + invalidateBottomGlow(); + } + + /** + * Sets the top edge effect color. + * + * @param color The color for the top edge effect. + * @see #setBottomEdgeEffectColor(int) + * @see #setEdgeEffectColor(int) + * @see #getTopEdgeEffectColor() + * @see #getBottomEdgeEffectColor() + */ + public void setTopEdgeEffectColor(@ColorInt int color) { + mEdgeGlowTop.setColor(color); + invalidateTopGlow(); + } + + /** + * Returns the top edge effect color. + * + * @return The top edge effect color. + * @see #setEdgeEffectColor(int) + * @see #setTopEdgeEffectColor(int) + * @see #setBottomEdgeEffectColor(int) + * @see #getBottomEdgeEffectColor() + */ + @ColorInt + public int getTopEdgeEffectColor() { + return mEdgeGlowTop.getColor(); + } + + /** + * Returns the bottom edge effect color. + * + * @return The bottom edge effect color. + * @see #setEdgeEffectColor(int) + * @see #setTopEdgeEffectColor(int) + * @see #setBottomEdgeEffectColor(int) + * @see #getTopEdgeEffectColor() + */ + @ColorInt + public int getBottomEdgeEffectColor() { + return mEdgeGlowBottom.getColor(); + } + + /** * Sets the recycler listener to be notified whenever a View is set aside in * the recycler for later reuse. This listener can be used to free resources * associated to the View. diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index ddff8581d568..c55f7d654548 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.database.DataSetObserver; +import android.os.Build; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; @@ -152,7 +153,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { /** * True if the data has changed since the last layout */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768524) boolean mDataChanged; /** diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 9bc055e7111b..7ed7aa293f21 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -126,7 +126,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private boolean mDropDownDismissedOnCompletion = true; private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; - private boolean mOpenBefore; + private MyWatcher mAutoCompleteTextWatcher; private Validator mValidator = null; @@ -302,7 +302,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe setFocusable(true); - addTextChangedListener(new MyWatcher()); + mAutoCompleteTextWatcher = new MyWatcher(); + addTextChangedListener(mAutoCompleteTextWatcher); mPassThroughClickListener = new PassThroughClickListener(); super.setOnClickListener(mPassThroughClickListener); @@ -872,45 +873,66 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return getText().length() >= mThreshold; } - /** - * This is used to watch for edits to the text view. Note that we call - * to methods on the auto complete text view class so that we can access - * private vars without going through thunks. - */ + + + /** This is used to watch for edits to the text view. */ private class MyWatcher implements TextWatcher { - public void afterTextChanged(Editable s) { - doAfterTextChanged(); - } + private boolean mOpenBefore; + public void beforeTextChanged(CharSequence s, int start, int count, int after) { - doBeforeTextChanged(); + if (mBlockCompletion) return; + + // when text is changed, inserted or deleted, we attempt to show + // the drop down + mOpenBefore = isPopupShowing(); + if (DEBUG) Log.v(TAG, "before text changed: open=" + mOpenBefore); + } + + public void afterTextChanged(Editable s) { + if (mBlockCompletion) return; + + // if the list was open before the keystroke, but closed afterwards, + // then something in the keystroke processing (an input filter perhaps) + // called performCompletion() and we shouldn't do any more processing. + if (DEBUG) { + Log.v(TAG, "after text changed: openBefore=" + mOpenBefore + + " open=" + isPopupShowing()); + } + + if (mOpenBefore && !isPopupShowing()) return; + + refreshAutoCompleteResults(); } + public void onTextChanged(CharSequence s, int start, int before, int count) { } } - @UnsupportedAppUsage + /** + * This function is deprecated. Please use {@link #refreshAutoCompleteResults} instead. + * Note: Remove {@link #mAutoCompleteTextWatcher} after removing this function. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void doBeforeTextChanged() { - if (mBlockCompletion) return; - - // when text is changed, inserted or deleted, we attempt to show - // the drop down - mOpenBefore = isPopupShowing(); - if (DEBUG) Log.v(TAG, "before text changed: open=" + mOpenBefore); + mAutoCompleteTextWatcher.beforeTextChanged(null, 0, 0, 0); } - @UnsupportedAppUsage + /** + * This function is deprecated. Please use {@link #refreshAutoCompleteResults} instead. + * Note: Remove {@link #mAutoCompleteTextWatcher} after removing this function. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void doAfterTextChanged() { - if (mBlockCompletion) return; - - // if the list was open before the keystroke, but closed afterwards, - // then something in the keystroke processing (an input filter perhaps) - // called performCompletion() and we shouldn't do any more processing. - if (DEBUG) Log.v(TAG, "after text changed: openBefore=" + mOpenBefore - + " open=" + isPopupShowing()); - if (mOpenBefore && !isPopupShowing()) { - return; - } + mAutoCompleteTextWatcher.afterTextChanged(null); + } + /** + * Refreshes the auto complete results. You usually shouldn't have to manually refresh the + * AutoCompleteResults as this is done automatically whenever the text changes. However if the + * results are not available and have to be fetched, you can call this function after fetching + * the results. + */ + public final void refreshAutoCompleteResults() { // the drop down is shown only when a minimum number of characters // was typed in the text view if (enoughToFilter()) { diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 7e42862558e4..fa0af78694ab 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -17,14 +17,15 @@ package android.widget; import android.annotation.ColorInt; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.os.Build; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -48,6 +49,12 @@ import android.view.animation.Interpolator; * {@link #draw(Canvas)} method.</p> */ public class EdgeEffect { + + /** + * The default blend mode used by {@link EdgeEffect}. + */ + public static final BlendMode DEFAULT_BLEND_MODE = BlendMode.SRC_ATOP; + @SuppressWarnings("UnusedDeclaration") private static final String TAG = "EdgeEffect"; @@ -108,7 +115,7 @@ public class EdgeEffect { private float mPullDistance; private final Rect mBounds = new Rect(); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769450) private final Paint mPaint = new Paint(); private float mRadius; private float mBaseGlowScale; @@ -128,7 +135,7 @@ public class EdgeEffect { a.recycle(); mPaint.setColor((themeColor & 0xffffff) | 0x33000000); mPaint.setStyle(Paint.Style.FILL); - mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); + mPaint.setBlendMode(DEFAULT_BLEND_MODE); mInterpolator = new DecelerateInterpolator(); } @@ -302,6 +309,22 @@ public class EdgeEffect { } /** + * Set or clear the blend mode. A blend mode defines how source pixels + * (generated by a drawing command) are composited with the destination pixels + * (content of the render target). + * <p /> + * Pass null to clear any previous blend mode. + * <p /> + * + * @see BlendMode + * + * @param blendmode May be null. The blend mode to be installed in the paint + */ + public void setBlendMode(@Nullable BlendMode blendmode) { + mPaint.setBlendMode(blendmode); + } + + /** * Return the color of this edge effect in argb. * @return The color of this edge effect in argb */ @@ -310,6 +333,20 @@ public class EdgeEffect { return mPaint.getColor(); } + + /** + * Returns the blend mode. A blend mode defines how source pixels + * (generated by a drawing command) are composited with the destination pixels + * (content of the render target). + * <p /> + * + * @return BlendMode + */ + @Nullable + public BlendMode getBlendMode() { + return mPaint.getBlendMode(); + } + /** * Draw into the provided canvas. Assumes that the canvas has been rotated * accordingly and the size has been set. The effect will be drawn the full diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index bf65ec0d5de3..bbbe369d5262 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -123,7 +123,7 @@ public class GridView extends AbsListView { private int mColumnWidth; @UnsupportedAppUsage private int mRequestedColumnWidth; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769395) private int mRequestedNumColumns; private View mReferenceView = null; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index e9c31db8110b..800b19cdd77e 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -138,7 +138,7 @@ public class ImageView extends View { private int mDrawableWidth; @UnsupportedAppUsage private int mDrawableHeight; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051687) private Matrix mDrawMatrix = null; // Avoid allocations... diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index f9564b44e825..25e5dd32c6b2 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -471,11 +471,24 @@ public class ListPopupWindow implements ShowableListMenu { * Specifies the anchor-relative bounds of the popup's transition * epicenter. * - * @param bounds anchor-relative bounds - * @hide + * @param bounds anchor-relative bounds, or {@code null} to use default epicenter + * + * @see #getEpicenterBounds() + */ + public void setEpicenterBounds(@Nullable Rect bounds) { + mEpicenterBounds = bounds != null ? new Rect(bounds) : null; + } + + /** + * Returns bounds which are used as a popup's epicenter + * of the enter and exit transitions. + * + * @return bounds relative to anchor view, or {@code null} if not set + * @see #setEpicenterBounds(Rect) */ - public void setEpicenterBounds(Rect bounds) { - mEpicenterBounds = bounds; + @Nullable + public Rect getEpicenterBounds() { + return mEpicenterBounds != null ? new Rect(mEpicenterBounds) : null; } /** diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index d5b1a3d69dfe..249f49956256 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -1013,7 +1013,7 @@ public final class Magnifier { } synchronized (mLock) { mRenderer.destroy(); - mSurfaceControl.destroy(); + mSurfaceControl.remove(); mSurfaceSession.kill(); mHandler.removeCallbacks(mMagnifierUpdater); if (mBitmap != null) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 705a371e483e..279829672c57 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -264,7 +264,7 @@ public class PopupWindow { private WeakReference<View> mAnchorRoot; private boolean mIsAnchorRootAttached; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private final OnScrollChangedListener mOnScrollChangedListener = this::alignToAnchor; private final View.OnLayoutChangeListener mOnLayoutChangeListener = @@ -476,23 +476,40 @@ public class PopupWindow { } /** - * Sets the bounds used as the epicenter of the enter and exit transitions. - * <p> - * Transitions use a point or Rect, referred to as the epicenter, to orient + * <p>Returns bounds which are used as a center of the enter and exit transitions.<p/> + * + * <p>Transitions use Rect, referred to as the epicenter, to orient * the direction of travel. For popup windows, the anchor view bounds are - * used as the default epicenter. - * <p> - * See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more - * information about how transition epicenters. + * used as the default epicenter.</p> + * + * <p>See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more + * information about how transition epicenters work.</p> + * + * @return bounds relative to anchor view, or {@code null} if not set + * @see #setEpicenterBounds(Rect) + */ + @Nullable + public Rect getEpicenterBounds() { + return mEpicenterBounds != null ? new Rect(mEpicenterBounds) : null; + } + + /** + * <p>Sets the bounds used as the epicenter of the enter and exit transitions.</p> + * + * <p>Transitions use Rect, referred to as the epicenter, to orient + * the direction of travel. For popup windows, the anchor view bounds are + * used as the default epicenter.</p> + * + * <p>See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more + * information about how transition epicenters work.</p> * * @param bounds the epicenter bounds relative to the anchor view, or * {@code null} to use the default epicenter - * @see #getTransitionEpicenter() - * @hide + * + * @see #getEpicenterBounds() */ - @UnsupportedAppUsage - public void setEpicenterBounds(Rect bounds) { - mEpicenterBounds = bounds; + public void setEpicenterBounds(@Nullable Rect bounds) { + mEpicenterBounds = bounds != null ? new Rect(bounds) : null; } private Transition getTransition(int resId) { @@ -865,12 +882,28 @@ public class PopupWindow { } /** - * Clip this popup window to the screen, but not to the containing window. + * <p>Indicates whether this popup will be clipped to the screen and not to the + * containing window<p/> * - * @param enabled True to clip to the screen. - * @hide + * @return true if popup will be clipped to the screen instead of the window, false otherwise + * + * @see #setClipToScreenEnabled(boolean) + */ + public boolean isClipToScreenEnabled() { + return mClipToScreen; + } + + /** + * <p>Clip this popup window to the screen, but not to the containing window.</p> + * + * <p>If the popup is showing, calling this method will take effect only + * the next time the popup is shown or through a manual call to one of + * the {@link #update()} methods.</p> + * + * @param enabled true to clip to the screen. + * + * @see #isClipToScreenEnabled() */ - @UnsupportedAppUsage public void setClipToScreenEnabled(boolean enabled) { mClipToScreen = enabled; } @@ -927,7 +960,8 @@ public class PopupWindow { * for positioning.</p> * * @return true if the window will always be positioned in screen coordinates. - * @hide + * + * @see #setLayoutInScreenEnabled(boolean) */ public boolean isLayoutInScreenEnabled() { return mLayoutInScreen; @@ -939,9 +973,9 @@ public class PopupWindow { * This will cause the popup to be positioned in absolute screen coordinates.</p> * * @param enabled true if the popup should always be positioned in screen coordinates - * @hide + * + * @see #isLayoutInScreenEnabled() */ - @UnsupportedAppUsage public void setLayoutInScreenEnabled(boolean enabled) { mLayoutInScreen = enabled; } @@ -1021,11 +1055,30 @@ public class PopupWindow { } /** - * Set whether this window is touch modal or if outside touches will be sent to - * other windows behind it. - * @hide + * <p>Indicates whether outside touches will be sent to this window + * or other windows behind it<p/> + * + * @return true if touches will be sent to this window, false otherwise + * + * @see #setTouchModal(boolean) + */ + public boolean isTouchModal() { + return !mNotTouchModal; + } + + /** + * <p>Set whether this window is touch modal or if outside touches will be sent to + * other windows behind it.<p/> + * + * <p>If the popup is showing, calling this method will take effect only + * the next time the popup is shown or through a manual call to one of + * the {@link #update()} methods.</p> + * + * @param touchModal true to sent all outside touches to this window, + * false to other windows behind it + * + * @see #isTouchModal() */ - @UnsupportedAppUsage public void setTouchModal(boolean touchModal) { mNotTouchModal = !touchModal; } @@ -1454,7 +1507,7 @@ public class PopupWindow { * * @param p the layout parameters of the popup's content view */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private void invokePopup(WindowManager.LayoutParams p) { if (mContext != null) { p.packageName = mContext.getPackageName(); @@ -2060,6 +2113,8 @@ public class PopupWindow { * <li>{@link #setInputMethodMode(int)}</li> * <li>{@link #setTouchable(boolean)}</li> * <li>{@link #setAnimationStyle(int)}</li> + * <li>{@link #setTouchModal(boolean)} (boolean)}</li> + * <li>{@link #setClipToScreenEnabled(boolean)}</li> * </ul> */ public void update() { diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index b60026810efb..30df5b5a09a3 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -20,6 +20,7 @@ import android.animation.ObjectAnimator; import android.annotation.InterpolatorRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.ColorStateList; @@ -170,12 +171,24 @@ public class ProgressBar extends View { /** Duration of smooth progress animations. */ private static final int PROGRESS_ANIM_DURATION = 80; - @UnsupportedAppUsage + /** + * Outside the framework, please use {@link ProgressBar#getMinWidth()} and + * {@link ProgressBar#setMinWidth(int)} instead of accessing these directly. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mMinWidth; int mMaxWidth; - @UnsupportedAppUsage + /** + * Outside the framework, please use {@link ProgressBar#getMinHeight()} and + * {@link ProgressBar#setMinHeight(int)} instead of accessing these directly. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mMinHeight; - @UnsupportedAppUsage + /** + * Outside the framework, please use {@link ProgressBar#getMaxHeight()} ()} and + * {@link ProgressBar#setMaxHeight(int)} (int)} instead of accessing these directly. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mMaxHeight; private int mProgress; @@ -198,7 +211,12 @@ public class ProgressBar extends View { private Drawable mIndeterminateDrawable; private Drawable mProgressDrawable; - @UnsupportedAppUsage + /** + * Outside the framework, instead of accessing this directly, please use + * {@link #getCurrentDrawable()}, {@link #setProgressDrawable(Drawable)}, + * {@link #setIndeterminateDrawable(Drawable)} and their tiled versions. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private Drawable mCurrentDrawable; private ProgressTintInfo mProgressTintInfo; @@ -393,6 +411,74 @@ public class ProgressBar extends View { } /** + * Sets the minimum width the progress bar can have. + * @param minWidth the minimum width to be set, in pixels + * @attr ref android.R.styleable#ProgressBar_minWidth + */ + public void setMinWidth(@Px int minWidth) { + mMinWidth = minWidth; + requestLayout(); + } + + /** + * @return the minimum width the progress bar can have, in pixels + */ + @Px public int getMinWidth() { + return mMinWidth; + } + + /** + * Sets the maximum width the progress bar can have. + * @param maxWidth the maximum width to be set, in pixels + * @attr ref android.R.styleable#ProgressBar_maxWidth + */ + public void setMaxWidth(@Px int maxWidth) { + mMaxWidth = maxWidth; + requestLayout(); + } + + /** + * @return the maximum width the progress bar can have, in pixels + */ + @Px public int getMaxWidth() { + return mMaxWidth; + } + + /** + * Sets the minimum height the progress bar can have. + * @param minHeight the minimum height to be set, in pixels + * @attr ref android.R.styleable#ProgressBar_minHeight + */ + public void setMinHeight(@Px int minHeight) { + mMinHeight = minHeight; + requestLayout(); + } + + /** + * @return the minimum height the progress bar can have, in pixels + */ + @Px public int getMinHeight() { + return mMinHeight; + } + + /** + * Sets the maximum height the progress bar can have. + * @param maxHeight the maximum height to be set, in pixels + * @attr ref android.R.styleable#ProgressBar_maxHeight + */ + public void setMaxHeight(@Px int maxHeight) { + mMaxHeight = maxHeight; + requestLayout(); + } + + /** + * @return the maximum height the progress bar can have, in pixels + */ + @Px public int getMaxHeight() { + return mMaxHeight; + } + + /** * Returns {@code true} if the target drawable needs to be tileified. * * @param dr the drawable to check @@ -1217,9 +1303,14 @@ public class ProgressBar extends View { } /** - * @return The drawable currently used to draw the progress bar + * Returns the drawable currently used to draw the progress bar. This will be + * either {@link #getProgressDrawable()} or {@link #getIndeterminateDrawable()} + * depending on whether the progress bar is in determinate or indeterminate mode. + * + * @return the drawable currently used to draw the progress bar */ - Drawable getCurrentDrawable() { + @Nullable + public Drawable getCurrentDrawable() { return mCurrentDrawable; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index fc4e9ec3caad..c3609038b08f 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -111,7 +111,7 @@ public class ScrollView extends FrameLayout { * layout is dirty. This prevents the scroll from being wrong if the child has not been * laid out before requesting focus. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769715) private View mChildToScrollTo = null; /** @@ -141,13 +141,13 @@ public class ScrollView extends FrameLayout { private boolean mSmoothScrollingEnabled = true; private int mTouchSlop; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051125) private int mMinimumVelocity; private int mMaximumVelocity; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.P, trackingBug = 124050903) private int mOverscrollDistance; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.P, trackingBug = 124050903) private int mOverflingDistance; private float mVerticalScrollFactor; @@ -1384,16 +1384,20 @@ public class ScrollView extends FrameLayout { * * @param child the View to scroll to */ - private void scrollToChild(View child) { - child.getDrawingRect(mTempRect); + public void scrollToDescendant(View child) { + if (!mIsLayoutDirty) { + child.getDrawingRect(mTempRect); - /* Offset from child's local coordinates to ScrollView coordinates */ - offsetDescendantRectToMyCoords(child, mTempRect); + /* Offset from child's local coordinates to ScrollView coordinates */ + offsetDescendantRectToMyCoords(child, mTempRect); - int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); - if (scrollDelta != 0) { - scrollBy(0, scrollDelta); + if (scrollDelta != 0) { + scrollBy(0, scrollDelta); + } + } else { + mChildToScrollTo = child; } } @@ -1488,7 +1492,7 @@ public class ScrollView extends FrameLayout { public void requestChildFocus(View child, View focused) { if (focused != null && focused.getRevealOnFocusHint()) { if (!mIsLayoutDirty) { - scrollToChild(focused); + scrollToDescendant(focused); } else { // The child may not be laid out yet, we can't compute the scroll yet mChildToScrollTo = focused; @@ -1569,7 +1573,7 @@ public class ScrollView extends FrameLayout { mIsLayoutDirty = false; // Give a child focus if it needs it if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { - scrollToChild(mChildToScrollTo); + scrollToDescendant(mChildToScrollTo); } mChildToScrollTo = null; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8626c6856e7c..d87600125a54 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -435,8 +435,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private ColorStateList mHintTextColor; private ColorStateList mLinkTextColor; @ViewDebug.ExportedProperty(category = "text") - @UnsupportedAppUsage + + /** + * {@link #setTextColor(int)} or {@link #getCurrentTextColor()} should be used instead. + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private int mCurTextColor; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mCurHintTextColor; private boolean mFreezesText; @@ -707,7 +712,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @UnsupportedAppUsage private ChangeWatcher mChangeWatcher; - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 123769451) private ArrayList<TextWatcher> mListeners; // display attributes @@ -1050,6 +1055,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int inputType = EditorInfo.TYPE_NULL; a = theme.obtainStyledAttributes( attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes); + saveAttributeDataForStyleable(context, com.android.internal.R.styleable.TextView, attrs, a, + defStyleAttr, defStyleRes); int firstBaselineToTopHeight = -1; int lastBaselineToBottomHeight = -1; int lineHeight = -1; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index ee96ae985331..119a015cd5ea 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -54,6 +54,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -186,9 +187,12 @@ public class ChooserActivity extends ResolverActivity { private @interface ContentPreviewType { } - private static final int CONTENT_PREVIEW_IMAGE = 0; - private static final int CONTENT_PREVIEW_FILE = 1; - private static final int CONTENT_PREVIEW_TEXT = 2; + // Starting at 1 since 0 is considered "undefined" for some of the database transformations + // of tron logs. + private static final int CONTENT_PREVIEW_IMAGE = 1; + private static final int CONTENT_PREVIEW_FILE = 2; + private static final int CONTENT_PREVIEW_TEXT = 3; + protected MetricsLogger mMetricsLogger; private final Handler mChooserHandler = new Handler() { @Override @@ -413,11 +417,12 @@ public class ChooserActivity extends ResolverActivity { } }); - MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); - mChooserShownTime = System.currentTimeMillis(); final long systemCost = mChooserShownTime - intentReceivedTime; - MetricsLogger.histogram(null, "system_cost_for_smart_sharing", (int) systemCost); + + getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN) + .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType()) + .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { final IntentFilter filter = getTargetIntentFilter(); @@ -470,6 +475,9 @@ public class ChooserActivity extends ResolverActivity { } int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); + + getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW) + .setSubtype(previewType)); displayContentPreview(previewType, targetIntent); } @@ -1180,6 +1188,13 @@ public class ChooserActivity extends ResolverActivity { } } + protected MetricsLogger getMetricsLogger() { + if (mMetricsLogger == null) { + mMetricsLogger = new MetricsLogger(); + } + return mMetricsLogger; + } + public class ChooserListController extends ResolverListController { public ChooserListController(Context context, PackageManager pm, @@ -1726,6 +1741,8 @@ public class ChooserActivity extends ResolverActivity { if (show != mShowServiceTargets) { mShowServiceTargets = show; notifyDataSetChanged(); + getMetricsLogger().write( + new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET)); } } @@ -1884,8 +1901,6 @@ public class ChooserActivity extends ResolverActivity { } if (startType == ChooserListAdapter.TARGET_SERVICE) { - holder.row.setBackgroundColor( - getColor(R.color.chooser_service_row_background_color)); int nextStartType = mChooserListAdapter.getPositionTargetType( getFirstRowPosition(rowPosition + 1)); int serviceSpacing = holder.row.getContext().getResources() diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java index c171fa6b25fd..258d081c6ad8 100644 --- a/core/java/com/android/internal/colorextraction/ColorExtractor.java +++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java @@ -22,7 +22,6 @@ import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.Context; import android.os.Trace; -import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -32,7 +31,6 @@ import com.android.internal.colorextraction.types.Tonal; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; /** * Class to process wallpaper colors and generate a tonal palette based on them. @@ -222,6 +220,7 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener public static class GradientColors { private int mMainColor; private int mSecondaryColor; + private int[] mColorPalette; private boolean mSupportsDarkText; public void setMainColor(int mainColor) { @@ -232,6 +231,10 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener mSecondaryColor = secondaryColor; } + public void setColorPalette(int[] colorPalette) { + mColorPalette = colorPalette; + } + public void setSupportsDarkText(boolean supportsDarkText) { mSupportsDarkText = supportsDarkText; } @@ -239,6 +242,7 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener public void set(GradientColors other) { mMainColor = other.mMainColor; mSecondaryColor = other.mSecondaryColor; + mColorPalette = other.mColorPalette; mSupportsDarkText = other.mSupportsDarkText; } @@ -250,6 +254,10 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener return mSecondaryColor; } + public int[] getColorPalette() { + return mColorPalette; + } + public boolean supportsDarkText() { return mSupportsDarkText; } @@ -283,4 +291,4 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener public interface OnColorsChangedListener { void onColorsChanged(ColorExtractor colorExtractor, int which); } -}
\ 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 3fd88dbb8704..d6a8934566b2 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -173,6 +173,7 @@ public class Tonal implements ExtractionType { Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY); float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f); float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f); + int[] colorPalette = getColorPalette(h, s, l); if (DEBUG) { StringBuilder builder = new StringBuilder("Tonal Palette - index: " + fitIndex + @@ -209,6 +210,7 @@ public class Tonal implements ExtractionType { // Normal colors: outColorsNormal.setMainColor(mainColor); outColorsNormal.setSecondaryColor(mainColor); + outColorsNormal.setColorPalette(colorPalette); // Dark colors: // Stops at 4th color, only lighter if dark text is supported @@ -222,6 +224,7 @@ public class Tonal implements ExtractionType { mainColor = getColorInt(primaryIndex, h, s, l); outColorsDark.setMainColor(mainColor); outColorsDark.setSecondaryColor(mainColor); + outColorsDark.setColorPalette(colorPalette); // Extra Dark: // Stay close to dark colors until dark text is supported @@ -235,6 +238,7 @@ public class Tonal implements ExtractionType { mainColor = getColorInt(primaryIndex, h, s, l); outColorsExtraDark.setMainColor(mainColor); outColorsExtraDark.setSecondaryColor(mainColor); + outColorsExtraDark.setColorPalette(colorPalette); outColorsNormal.setSupportsDarkText(supportsDarkText); outColorsDark.setSupportsDarkText(supportsDarkText); @@ -262,16 +266,19 @@ public class Tonal implements ExtractionType { * @param inWallpaperColors Colors to read. * @param outGradientColors Destination. */ - public static void applyFallback(@Nullable WallpaperColors inWallpaperColors, + public void applyFallback(@Nullable WallpaperColors inWallpaperColors, @NonNull GradientColors outGradientColors) { boolean light = inWallpaperColors != null && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; final int color = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK; + final float[] hsl = new float[3]; + ColorUtils.colorToHSL(color, hsl); outGradientColors.setMainColor(color); outGradientColors.setSecondaryColor(color); outGradientColors.setSupportsDarkText(light); + outGradientColors.setColorPalette(getColorPalette(findTonalPalette(hsl[0], hsl[1]))); } private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) { @@ -281,6 +288,19 @@ public class Tonal implements ExtractionType { return ColorUtils.HSLToColor(mTmpHSL); } + private int[] getColorPalette(float[] h, float[] s, float[] l) { + int[] colorPalette = new int[h.length]; + for (int i = 0; i < colorPalette.length; i++) { + colorPalette[i] = getColorInt(i, h, s, l); + } + return colorPalette; + } + + private int[] getColorPalette(TonalPalette palette) { + return getColorPalette(palette.h, palette.s, palette.l); + } + + /** * Checks if a given color exists in the blacklist * @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1) @@ -598,4 +618,4 @@ public class Tonal implements ExtractionType { return numbers; } } -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 650a19426d8e..52e1748c621c 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6117,8 +6117,6 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<N; i++) { final int uid = mapUid(ws.get(i)); noteFullWifiLockAcquiredLocked(uid); - StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), - StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -6127,9 +6125,6 @@ public class BatteryStatsImpl extends BatteryStats { final WorkChain workChain = workChains.get(i); final int uid = mapUid(workChain.getAttributionUid()); noteFullWifiLockAcquiredLocked(uid); - StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), - StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON); } } } @@ -6139,8 +6134,6 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<N; i++) { final int uid = mapUid(ws.get(i)); noteFullWifiLockReleasedLocked(uid); - StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), - StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -6149,9 +6142,6 @@ public class BatteryStatsImpl extends BatteryStats { final WorkChain workChain = workChains.get(i); final int uid = mapUid(workChain.getAttributionUid()); noteFullWifiLockReleasedLocked(uid); - StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), - StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF); } } } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index b881aef221ce..40d78688cb4c 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -99,6 +99,11 @@ public final class Zygote { */ public static final int PROFILE_FROM_SHELL = 1 << 15; + /* + * Enable using the ART app image startup cache + */ + public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ @@ -249,14 +254,14 @@ public final class Zygote { public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, - String packageName, String[] packagesForUID, String[] visibleVolIDs) { + String packageName, String[] packagesForUID, String[] visibleVolIDs, String sandboxId) { ZygoteHooks.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName, - packagesForUID, visibleVolIDs); + packagesForUID, visibleVolIDs, sandboxId); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); @@ -271,7 +276,8 @@ public final class Zygote { private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs); + String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs, + String sandboxId); /** * Specialize a Blastula instance. The current VM must have been started @@ -297,11 +303,11 @@ public final class Zygote { public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, - String[] packagesForUID, String[] visibleVolIDs) { + String[] packagesForUID, String[] visibleVolIDs, String sandboxId) { nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, - packageName, packagesForUID, visibleVolIDs); + packageName, packagesForUID, visibleVolIDs, sandboxId); // Enable tracing as soon as possible for the child process. Trace.setTracingEnabled(true, runtimeFlags); @@ -321,7 +327,7 @@ public final class Zygote { private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, - String[] packagesForUID, String[] visibleVolIDs); + String[] packagesForUID, String[] visibleVolIDs, String sandboxId); /** * Called to do any initialization before starting an application. @@ -633,7 +639,7 @@ public final class Zygote { args.mRuntimeFlags, rlimits, args.mMountExternal, args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir, args.mPackageName, - args.mPackagesForUid, args.mVisibleVolIds); + args.mPackagesForUid, args.mVisibleVolIds, args.mSandboxId); if (args.mNiceName != null) { Process.setArgV0(args.mNiceName); diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index 24a08ca5b1e0..e6bcd37ad3e5 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -119,6 +119,9 @@ class ZygoteArguments { /** from --visible-vols */ String[] mVisibleVolIds; + /** from --sandbox-id */ + String mSandboxId; + /** * Any args after and including the first non-option arg (or after a '--') */ @@ -385,6 +388,11 @@ class ZygoteArguments { mPackagesForUid = arg.substring(arg.indexOf('=') + 1).split(","); } else if (arg.startsWith("--visible-vols=")) { mVisibleVolIds = arg.substring(arg.indexOf('=') + 1).split(","); + } else if (arg.startsWith("--sandbox-id=")) { + if (mSandboxId != null) { + throw new IllegalArgumentException("Duplicate arg specified"); + } + mSandboxId = arg.substring(arg.indexOf('=') + 1); } else { break; } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 9ba56b8c0f4e..9cf7e2770e86 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -258,7 +258,7 @@ class ZygoteConnection { parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mPackageName, - parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds); + parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds, parsedArgs.mSandboxId); try { if (pid == 0) { @@ -334,9 +334,14 @@ class ZygoteConnection { } } - private class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger { + private static class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger { private final MetricsLogger mMetricsLogger = new MetricsLogger(); + private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger(); + + public static HiddenApiUsageLogger getInstance() { + return HiddenApiUsageLogger.sInstance; + } public void hiddenApiUsed(String packageName, String signature, int accessMethod, boolean accessDenied) { @@ -370,7 +375,7 @@ class ZygoteConnection { private void handleHiddenApiAccessLogSampleRate(int samplingRate) { try { ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate); - ZygoteInit.setHiddenApiUsageLogger(new HiddenApiUsageLogger()); + ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance()); mSocketOutStream.writeInt(0); } catch (IOException ioe) { throw new IllegalStateException("Error writing to command socket", ioe); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9f23797d6ccc..e132abd7e4cb 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -41,6 +41,7 @@ import android.system.OsConstants; import android.system.StructCapUserData; import android.system.StructCapUserHeader; import android.text.Hyphenator; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -84,6 +85,8 @@ public class ZygoteInit { private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; + private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE = + "persist.device_config.runtime_native.use_app_image_startup_cache"; private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020; private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030; @@ -705,6 +708,13 @@ public class ZygoteInit { parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER; } + String use_app_image_cache = SystemProperties.get( + PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, ""); + // Property defaults to true currently. + if (!TextUtils.isEmpty(use_app_image_cache) && !use_app_image_cache.equals("false")) { + parsedArgs.mRuntimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE; + } + /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.mUid, parsedArgs.mGid, diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 397df56a809c..b04ebec77a92 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -691,6 +691,15 @@ public class ArrayUtils { return result; } + public static boolean startsWith(byte[] cur, byte[] val) { + if (cur == null || val == null) return false; + if (cur.length < val.length) return false; + for (int i = 0; i < val.length; i++) { + if (cur[i] != val[i]) return false; + } + return true; + } + /** * Returns the first element from the array for which * condition {@code predicate} is true, or null if there is no such element diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index b7e656bc9278..ee8637d8c773 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -17,15 +17,12 @@ package com.android.internal.widget; -import com.android.internal.R; - import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.metrics.LogMaker; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -45,8 +42,13 @@ import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.OverScroller; +import com.android.internal.R; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + public class ResolverDrawerLayout extends ViewGroup { private static final String TAG = "ResolverDrawerLayout"; + private MetricsLogger mMetricsLogger; /** * Max width of the whole drawer layout @@ -496,6 +498,9 @@ public class ResolverDrawerLayout extends ViewGroup { final boolean isCollapsedNew = newPos != 0; if (isCollapsedOld != isCollapsedNew) { onCollapsedChanged(isCollapsedNew); + getMetricsLogger().write( + new LogMaker(MetricsEvent.ACTION_SHARESHEET_COLLAPSED_CHANGED) + .setSubtype(isCollapsedNew ? 1 : 0)); } postInvalidateOnAnimation(); return dy; @@ -1037,4 +1042,11 @@ public class ResolverDrawerLayout extends ViewGroup { dispatchOnDismissed(); } } + + private MetricsLogger getMetricsLogger() { + if (mMetricsLogger == null) { + mMetricsLogger = new MetricsLogger(); + } + return mMetricsLogger; + } } diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java index 1b4049212255..b4610bda2cd9 100644 --- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java +++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.SyncAdapterType; import android.os.Environment; import android.os.ParcelFileDescriptor; +import android.os.UserHandle; import android.util.Log; import org.json.JSONArray; @@ -48,6 +49,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Helper for backing up account sync settings (whether or not a service should be synced). The @@ -76,15 +78,17 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { private static final String KEY_AUTHORITY_NAME = "name"; private static final String KEY_AUTHORITY_SYNC_STATE = "syncState"; private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled"; - private static final String STASH_FILE = Environment.getDataDirectory() - + "/backup/unadded_account_syncsettings.json"; + private static final String STASH_FILE = "/backup/unadded_account_syncsettings.json"; private Context mContext; private AccountManager mAccountManager; + private final int mUserId; - public AccountSyncSettingsBackupHelper(Context context) { + public AccountSyncSettingsBackupHelper(Context context, int userId) { mContext = context; mAccountManager = AccountManager.get(mContext); + + mUserId = userId; } /** @@ -94,7 +98,7 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput output, ParcelFileDescriptor newState) { try { - JSONObject dataJSON = serializeAccountSyncSettingsToJSON(); + JSONObject dataJSON = serializeAccountSyncSettingsToJSON(mUserId); if (DEBUG) { Log.d(TAG, "Account sync settings JSON: " + dataJSON); @@ -123,10 +127,9 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { /** * Fetch and serialize Account and authority information as a JSON Array. */ - private JSONObject serializeAccountSyncSettingsToJSON() throws JSONException { - Account[] accounts = mAccountManager.getAccounts(); - SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( - mContext.getUserId()); + private JSONObject serializeAccountSyncSettingsToJSON(int userId) throws JSONException { + Account[] accounts = mAccountManager.getAccountsAsUser(userId); + SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(userId); // Create a map of Account types to authorities. Later this will make it easier for us to // generate our JSON. @@ -146,7 +149,8 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { // Generate JSON. JSONObject backupJSON = new JSONObject(); backupJSON.put(KEY_VERSION, JSON_FORMAT_VERSION); - backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomatically()); + backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomaticallyAsUser( + userId)); JSONArray accountJSONArray = new JSONArray(); for (Account account : accounts) { @@ -165,8 +169,9 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { // Add authorities for this Account type and check whether or not sync is enabled. JSONArray authoritiesJSONArray = new JSONArray(); for (String authority : authorities) { - int syncState = ContentResolver.getIsSyncable(account, authority); - boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority); + int syncState = ContentResolver.getIsSyncableAsUser(account, authority, userId); + boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority, + userId); JSONObject authorityJSON = new JSONObject(); authorityJSON.put(KEY_AUTHORITY_NAME, authority); @@ -254,17 +259,18 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { boolean masterSyncEnabled = dataJSON.getBoolean(KEY_MASTER_SYNC_ENABLED); JSONArray accountJSONArray = dataJSON.getJSONArray(KEY_ACCOUNTS); - boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically(); + boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomaticallyAsUser( + mUserId); if (currentMasterSyncEnabled) { // Disable master sync to prevent any syncs from running. - ContentResolver.setMasterSyncAutomatically(false); + ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId); } try { - restoreFromJsonArray(accountJSONArray); + restoreFromJsonArray(accountJSONArray, mUserId); } finally { // Set the master sync preference to the value from the backup set. - ContentResolver.setMasterSyncAutomatically(masterSyncEnabled); + ContentResolver.setMasterSyncAutomaticallyAsUser(masterSyncEnabled, mUserId); } Log.i(TAG, "Restore successful."); } catch (IOException | JSONException e) { @@ -272,9 +278,9 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { } } - private void restoreFromJsonArray(JSONArray accountJSONArray) + private void restoreFromJsonArray(JSONArray accountJSONArray, int userId) throws JSONException { - HashSet<Account> currentAccounts = getAccounts(); + Set<Account> currentAccounts = getAccounts(userId); JSONArray unaddedAccountsJSONArray = new JSONArray(); for (int i = 0; i < accountJSONArray.length(); i++) { JSONObject accountJSON = (JSONObject) accountJSONArray.get(i); @@ -292,14 +298,14 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { // yet won't be restored. if (currentAccounts.contains(account)) { if (DEBUG) Log.i(TAG, "Restoring Sync Settings for" + accountName); - restoreExistingAccountSyncSettingsFromJSON(accountJSON); + restoreExistingAccountSyncSettingsFromJSON(accountJSON, userId); } else { unaddedAccountsJSONArray.put(accountJSON); } } if (unaddedAccountsJSONArray.length() > 0) { - try (FileOutputStream fOutput = new FileOutputStream(STASH_FILE)) { + try (FileOutputStream fOutput = new FileOutputStream(getStashFile(userId))) { String jsonString = unaddedAccountsJSONArray.toString(); DataOutputStream out = new DataOutputStream(fOutput); out.writeUTF(jsonString); @@ -308,18 +314,20 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { Log.e(TAG, "unable to write the sync settings to the stash file", ioe); } } else { - File stashFile = new File(STASH_FILE); - if (stashFile.exists()) stashFile.delete(); + File stashFile = getStashFile(userId); + if (stashFile.exists()) { + stashFile.delete(); + } } } /** * Restore SyncSettings for all existing accounts from a stashed backup-set */ - private void accountAddedInternal() { + private void accountAddedInternal(int userId) { String jsonString; - try (FileInputStream fIn = new FileInputStream(new File(STASH_FILE))) { + try (FileInputStream fIn = new FileInputStream(getStashFile(userId))) { DataInputStream in = new DataInputStream(fIn); jsonString = in.readUTF(); } catch (FileNotFoundException fnfe) { @@ -333,7 +341,7 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { try { JSONArray unaddedAccountsJSONArray = new JSONArray(jsonString); - restoreFromJsonArray(unaddedAccountsJSONArray); + restoreFromJsonArray(unaddedAccountsJSONArray, userId); } catch (JSONException jse) { // Malformed jsonString Log.e(TAG, "there was an error with the stashed sync settings", jse); @@ -343,9 +351,10 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { /** * Restore SyncSettings for all existing accounts from a stashed backup-set */ - public static void accountAdded(Context context) { - AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context); - helper.accountAddedInternal(); + public static void accountAdded(Context context, int userId) { + AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context, + userId); + helper.accountAddedInternal(userId); } /** @@ -353,9 +362,9 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { * * @return Accounts in a HashSet. */ - private HashSet<Account> getAccounts() { - Account[] accounts = mAccountManager.getAccounts(); - HashSet<Account> accountHashSet = new HashSet<Account>(); + private Set<Account> getAccounts(int userId) { + Account[] accounts = mAccountManager.getAccountsAsUser(userId); + Set<Account> accountHashSet = new HashSet<Account>(); for (Account account : accounts) { accountHashSet.add(account); } @@ -391,7 +400,7 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { * initialization sync, while an adapter that the user had off will be off until the user * enables it on this device at which point it will get an initialization sync. */ - private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON) + private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON, int userId) throws JSONException { // Restore authorities. JSONArray authorities = accountJSON.getJSONArray(KEY_ACCOUNT_AUTHORITIES); @@ -406,14 +415,15 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { int wasSyncable = authority.getInt(KEY_AUTHORITY_SYNC_STATE); ContentResolver.setSyncAutomaticallyAsUser( - account, authorityName, wasSyncEnabled, 0 /* user Id */); + account, authorityName, wasSyncEnabled, userId); if (!wasSyncEnabled) { - ContentResolver.setIsSyncable( + ContentResolver.setIsSyncableAsUser( account, authorityName, wasSyncable == 0 ? - 0 /* not syncable */ : 2 /* syncable but needs initialization */); + 0 /* not syncable */ : 2 /* syncable but needs initialization */, + userId); } } } @@ -422,4 +432,10 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { public void writeNewStateDescription(ParcelFileDescriptor newState) { } -}
\ No newline at end of file + + private static File getStashFile(int userId) { + File baseDir = userId == UserHandle.USER_SYSTEM ? Environment.getDataDirectory() + : Environment.getDataSystemCeDirectory(userId); + return new File(baseDir, STASH_FILE); + } +} diff --git a/core/java/com/android/server/backup/NotificationBackupHelper.java b/core/java/com/android/server/backup/NotificationBackupHelper.java index 0d225e87d77b..7d4f8f79aa63 100644 --- a/core/java/com/android/server/backup/NotificationBackupHelper.java +++ b/core/java/com/android/server/backup/NotificationBackupHelper.java @@ -18,9 +18,7 @@ package com.android.server.backup; import android.app.INotificationManager; import android.app.backup.BlobBackupHelper; -import android.content.Context; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.Log; import android.util.Slog; @@ -34,9 +32,11 @@ public class NotificationBackupHelper extends BlobBackupHelper { // Key under which the payload blob is stored static final String KEY_NOTIFICATIONS = "notifications"; - public NotificationBackupHelper(Context context) { + private final int mUserId; + + public NotificationBackupHelper(int userId) { super(BLOB_VERSION, KEY_NOTIFICATIONS); - // context is currently unused + mUserId = userId; } @Override @@ -46,8 +46,7 @@ public class NotificationBackupHelper extends BlobBackupHelper { try { INotificationManager nm = INotificationManager.Stub.asInterface( ServiceManager.getService("notification")); - // TODO: http://b/22388012 - newPayload = nm.getBackupPayload(UserHandle.USER_SYSTEM); + newPayload = nm.getBackupPayload(mUserId); } catch (Exception e) { // Treat as no data Slog.e(TAG, "Couldn't communicate with notification manager"); @@ -67,8 +66,7 @@ public class NotificationBackupHelper extends BlobBackupHelper { try { INotificationManager nm = INotificationManager.Stub.asInterface( ServiceManager.getService("notification")); - // TODO: http://b/22388012 - nm.applyRestore(payload, UserHandle.USER_SYSTEM); + nm.applyRestore(payload, mUserId); } catch (Exception e) { Slog.e(TAG, "Couldn't communicate with notification manager"); } diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 8878421a8016..35e8f56cf36d 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -81,7 +81,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY; private static final Set<String> sEligibleForMultiUser = Sets.newArraySet( - PERMISSION_HELPER); + PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER); private int mUserId = UserHandle.USER_SYSTEM; @@ -91,9 +91,9 @@ public class SystemBackupAgent extends BackupAgentHelper { mUserId = user.getIdentifier(); - addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); + addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this, mUserId)); addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); - addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); + addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId)); addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId)); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp index b52f137da7d6..2abd95020f1c 100644 --- a/core/jni/android_opengl_EGL15.cpp +++ b/core/jni/android_opengl_EGL15.cpp @@ -194,6 +194,7 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { if (obj == NULL){ jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + return nullptr; } jlong handle = _env->CallLongMethod(obj, mid); @@ -254,6 +255,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsyncClass, eglsyncConstructor, _returnValue); } @@ -335,6 +337,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } @@ -381,6 +384,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue); } @@ -448,6 +452,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } @@ -456,8 +461,11 @@ exit: static jobject android_eglCreatePlatformPixmapSurface (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) { - jniThrowException(_env, "java/lang/UnsupportedOperationException", - "eglCreatePlatformPixmapSurface"); + if ((true)) { + jniThrowException(_env, "java/lang/UnsupportedOperationException", + "eglCreatePlatformPixmapSurface"); + return nullptr; + } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0); } @@ -523,6 +531,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglimageClass, eglimageConstructor, _returnValue); } diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 06625b36f162..e2e66ceb6fbe 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -32,6 +32,17 @@ void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str()); } +void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, + jstring driverVersionName, jlong driverVersionCode, + jstring appPackageName) { + ScopedUtfChars driverPackageNameChars(env, driverPackageName); + ScopedUtfChars driverVersionNameChars(env, driverVersionName); + ScopedUtfChars appPackageNameChars(env, appPackageName); + android::GraphicsEnv::getInstance().setGpuStats(driverPackageNameChars.c_str(), + driverVersionNameChars.c_str(), + driverVersionCode, appPackageNameChars.c_str()); +} + void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring devOptIn, jobject rulesFd, jlong rulesOffset, jlong rulesLength) { ScopedUtfChars pathChars(env, path); @@ -68,6 +79,7 @@ void setDebugLayersGLES_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) }, { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, + { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) }, diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 4101c04162af..a212f47c0104 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -104,6 +104,12 @@ static struct configuration_offsets_t { jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; +static struct arraymap_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; +} gArrayMapOffsets; + jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- @@ -326,6 +332,50 @@ static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) { return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr)); } +static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jstring package_name) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ScopedUtfChars package_name_utf8(env, package_name); + CHECK(package_name_utf8.c_str() != nullptr); + const std::string std_package_name(package_name_utf8.c_str()); + const std::unordered_map<std::string, std::string>* map = nullptr; + + assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) { + if (this_package_name == std_package_name) { + map = assetmanager->GetOverlayableMapForPackage(package_id); + } + }); + + if (map == nullptr) { + return nullptr; + } + + jobject array_map = env->NewObject(gArrayMapOffsets.classObject, gArrayMapOffsets.constructor); + if (array_map == nullptr) { + return nullptr; + } + + for (const auto& iter : *map) { + jstring name = env->NewStringUTF(iter.first.c_str()); + if (env->ExceptionCheck()) { + return nullptr; + } + + jstring actor = env->NewStringUTF(iter.second.c_str()); + if (env->ExceptionCheck()) { + env->DeleteLocalRef(name); + return nullptr; + } + + env->CallObjectMethod(array_map, gArrayMapOffsets.put, name, actor); + + env->DeleteLocalRef(name); + env->DeleteLocalRef(actor); + } + + return array_map; +} + static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset, jlongArray out_offsets) { off64_t start_offset, length; @@ -1105,6 +1155,46 @@ static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, j return array; } +static jintArray NativeAttributeResolutionStack( + JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong theme_ptr, jint xml_style_res, + jint def_style_attr, jint def_style_resid) { + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + // Load default style from attribute, if specified... + uint32_t def_style_flags = 0u; + if (def_style_attr != 0) { + Res_value value; + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + def_style_resid = value.data; + } + } + } + + auto style_stack = assetmanager->GetBagResIdStack(xml_style_res); + auto def_style_stack = assetmanager->GetBagResIdStack(def_style_resid); + + jintArray array = env->NewIntArray(style_stack.size() + def_style_stack.size()); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < style_stack.size(); i++) { + jint attr_resid = style_stack[i]; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + for (uint32_t i = 0; i < def_style_stack.size(); i++) { + jint attr_resid = def_style_stack[i]; + env->SetIntArrayRegion(array, style_stack.size() + i, 1, &attr_resid); + } + return array; +} + static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { @@ -1456,6 +1546,7 @@ static const JNINativeMethod gAssetManagerMethods[] = { (void*)NativeGetSizeConfigurations}, // Style attribute related methods. + {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack}, {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, @@ -1483,6 +1574,8 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;", (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid}, + {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;", + (void*)NativeGetOverlayableMap}, // Global management/debug methods. {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, @@ -1534,6 +1627,14 @@ int register_android_content_AssetManager(JNIEnv* env) { gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap"); + gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass); + gArrayMapOffsets.constructor = + GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "<init>", "()V"); + gArrayMapOffsets.put = + GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods)); } diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index c1b5aaec1106..191472d086f6 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -59,8 +59,8 @@ private: sp<MessageQueue> mMessageQueue; DisplayEventReceiver mReceiver; - virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); - virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; + void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; }; @@ -84,28 +84,30 @@ void NativeDisplayEventReceiver::dispose() { DisplayEventDispatcher::dispose(); } -void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) { +void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, + uint32_t count) { JNIEnv* env = AndroidRuntime::getJNIEnv(); ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking vsync handler.", this); env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count); + gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count); ALOGV("receiver %p ~ Returned from vsync handler.", this); } mMessageQueue->raiseAndClearException(env, "dispatchVsync"); } -void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) { +void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, + bool connected) { JNIEnv* env = AndroidRuntime::getJNIEnv(); ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking hotplug handler.", this); env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected); + gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected); ALOGV("receiver %p ~ Returned from hotplug handler.", this); } @@ -175,9 +177,9 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env, - gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JII)V"); + gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V"); gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, - gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JIZ)V"); + gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); return res; } diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 67a56ae2b52d..464f24901eb1 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -58,6 +58,8 @@ namespace android { +using ui::Dataspace; + static const char* const OutOfResourcesException = "android/view/Surface$OutOfResourcesException"; @@ -132,6 +134,7 @@ int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) { case PublicFormat::JPEG: case PublicFormat::DEPTH_POINT_CLOUD: case PublicFormat::DEPTH_JPEG: + case PublicFormat::HEIC: return HAL_PIXEL_FORMAT_BLOB; case PublicFormat::DEPTH16: return HAL_PIXEL_FORMAT_Y16; @@ -146,32 +149,44 @@ int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) { android_dataspace android_view_Surface_mapPublicFormatToHalDataspace( PublicFormat f) { + Dataspace dataspace; switch(f) { case PublicFormat::JPEG: - return HAL_DATASPACE_V0_JFIF; + dataspace = Dataspace::V0_JFIF; + break; case PublicFormat::DEPTH_POINT_CLOUD: case PublicFormat::DEPTH16: case PublicFormat::RAW_DEPTH: - return HAL_DATASPACE_DEPTH; + dataspace = Dataspace::DEPTH; + break; case PublicFormat::RAW_SENSOR: case PublicFormat::RAW_PRIVATE: case PublicFormat::RAW10: case PublicFormat::RAW12: - return HAL_DATASPACE_ARBITRARY; + dataspace = Dataspace::ARBITRARY; + break; case PublicFormat::YUV_420_888: case PublicFormat::NV21: case PublicFormat::YV12: - return HAL_DATASPACE_V0_JFIF; + dataspace = Dataspace::V0_JFIF; + break; case PublicFormat::DEPTH_JPEG: - return static_cast<android_dataspace> (HAL_DATASPACE_DYNAMIC_DEPTH); + dataspace = Dataspace::DYNAMIC_DEPTH; + break; + case PublicFormat::HEIC: + dataspace = Dataspace::HEIF; + break; default: // Most formats map to UNKNOWN - return HAL_DATASPACE_UNKNOWN; + dataspace = Dataspace::UNKNOWN; + break; } + return static_cast<android_dataspace>(dataspace); } PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( int format, android_dataspace dataSpace) { + Dataspace ds = static_cast<Dataspace>(dataSpace); switch(format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: @@ -187,8 +202,8 @@ PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( // Enums overlap in both name and value return static_cast<PublicFormat>(format); case HAL_PIXEL_FORMAT_RAW16: - switch (dataSpace) { - case HAL_DATASPACE_DEPTH: + switch (ds) { + case Dataspace::DEPTH: return PublicFormat::RAW_DEPTH; default: return PublicFormat::RAW_SENSOR; @@ -210,8 +225,8 @@ PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( return PublicFormat::PRIVATE; case HAL_PIXEL_FORMAT_Y16: // Dataspace-dependent - switch (dataSpace) { - case HAL_DATASPACE_DEPTH: + switch (ds) { + case Dataspace::DEPTH: return PublicFormat::DEPTH16; default: // Assume non-depth Y16 is just Y16. @@ -220,11 +235,13 @@ PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( break; case HAL_PIXEL_FORMAT_BLOB: // Dataspace-dependent - switch (dataSpace) { - case HAL_DATASPACE_DEPTH: + switch (ds) { + case Dataspace::DEPTH: return PublicFormat::DEPTH_POINT_CLOUD; - case HAL_DATASPACE_V0_JFIF: + case Dataspace::V0_JFIF: return PublicFormat::JPEG; + case Dataspace::HEIF: + return PublicFormat::HEIC; default: if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) { return PublicFormat::DEPTH_JPEG; diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index fad2fe0d3845..6b8d8b1bb91f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -178,12 +178,13 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, static void nativeRelease(JNIEnv* env, jclass clazz, jlong nativeObject) { sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject)); + ctrl->release(); ctrl->decStrong((void *)nativeCreate); } static void nativeDestroy(JNIEnv* env, jclass clazz, jlong nativeObject) { sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject)); - ctrl->clear(); + ctrl->destroy(); ctrl->decStrong((void *)nativeCreate); } @@ -483,8 +484,29 @@ static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->setLayerStack(ctrl, layerStack); } -static jobject nativeGetBuiltInDisplay(JNIEnv* env, jclass clazz, jint id) { - sp<IBinder> token(SurfaceComposerClient::getBuiltInDisplay(id)); +static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { + const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds(); + jlongArray array = env->NewLongArray(displayIds.size()); + if (array == nullptr) { + jniThrowException(env, "java/lang/OutOfMemoryError", nullptr); + return nullptr; + } + + if (displayIds.empty()) { + return array; + } + + jlong* values = env->GetLongArrayElements(array, 0); + for (size_t i = 0; i < displayIds.size(); ++i) { + values[i] = static_cast<jlong>(displayIds[i]); + } + + env->ReleaseLongArrayElements(array, values, 0); + return array; +} + +static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) { + sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(physicalDisplayId); return javaObjectForIBinder(env, token); } @@ -1145,8 +1167,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetCornerRadius }, {"nativeSetLayerStack", "(JJI)V", (void*)nativeSetLayerStack }, - {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;", - (void*)nativeGetBuiltInDisplay }, + {"nativeGetPhysicalDisplayIds", "()[J", + (void*)nativeGetPhysicalDisplayIds }, + {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;", + (void*)nativeGetPhysicalDisplayToken }, {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;", (void*)nativeCreateDisplay }, {"nativeDestroyDisplay", "(Landroid/os/IBinder;)V", @@ -1314,4 +1338,4 @@ int register_android_view_SurfaceControl(JNIEnv* env) return err; } -}; +} // namespace android diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 2c058cd0d6fd..8c7363021d3b 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -1202,12 +1202,12 @@ static const JNINativeMethod gMethods[] = { static JavaVM* mJvm = nullptr; -static void attachRenderThreadToJvm() { +static void attachRenderThreadToJvm(const char* name) { LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??"); JavaVMAttachArgs args; args.version = JNI_VERSION_1_4; - args.name = NULL; + args.name = name; args.group = NULL; JNIEnv* env; mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 5a65028469a8..d04db92294d7 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -598,55 +598,80 @@ static int UnmountTree(const char* path) { return 0; } +static void CreateDir(const std::string& dir, + mode_t mode, uid_t uid, gid_t gid, + fail_fn_t fail_fn) { + if (TEMP_FAILURE_RETRY(access(dir.c_str(), F_OK)) == 0) { + return; + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to stat %s: %s", dir.c_str(), strerror(errno))); + } + if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) { + fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s: %s", + dir.c_str(), strerror(errno))); + } +} + static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) { // Create /mnt/user/0/package/<package-name> userid_t user_id = multiuser_get_user_id(uid); std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id); - if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) { - fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str())); - } + CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn); StringAppendF(&pkg_sandbox_dir, "/package"); - if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) { - fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str())); - } + CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn); StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str()); - if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) { - fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str())); - } + CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn); } static void BindMount(const std::string& sourceDir, const std::string& targetDir, fail_fn_t fail_fn) { if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(), nullptr, - MS_BIND | MS_REC, nullptr)) == -1) { + MS_BIND, nullptr)) == -1) { fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s", sourceDir.c_str(), targetDir.c_str(), strerror(errno))); } - - if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(), nullptr, - MS_SLAVE | MS_REC, nullptr)) == -1) { - fail_fn(CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str())); - } } static void MountPkgSpecificDir(const std::string& mntSourceRoot, const std::string& mntTargetRoot, const std::string& packageName, + uid_t uid, const char* dirName, fail_fn_t fail_fn) { std::string mntSourceDir = StringPrintf("%s/Android/%s/%s", mntSourceRoot.c_str(), dirName, packageName.c_str()); + CreateDir(mntSourceDir, 0755, uid, uid, fail_fn); + std::string mntTargetDir = StringPrintf("%s/Android/%s/%s", mntTargetRoot.c_str(), dirName, packageName.c_str()); + CreateDir(mntTargetDir, 0755, uid, uid, fail_fn); BindMount(mntSourceDir, mntTargetDir, fail_fn); } + +static void createPkgSpecificDirRoots(const std::string& parentDir, + bool createSandbox, + mode_t mode, uid_t uid, gid_t gid, + fail_fn_t fail_fn) { + std::string androidDir = StringPrintf("%s/Android", parentDir.c_str()); + CreateDir(androidDir, mode, uid, gid, fail_fn); + std::vector<std::string> dirs = {"data", "media", "obb"}; + if (createSandbox) { + dirs.push_back("sandbox"); + } + for (auto& dir : dirs) { + std::string path = StringPrintf("%s/%s", androidDir.c_str(), dir.c_str()); + CreateDir(path, mode, uid, gid, fail_fn); + } +} + static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames, const std::vector<std::string>& volumeLabels, - bool mountAllObbs, userid_t userId, fail_fn_t fail_fn) { + bool mountAllObbs, const std::string& sandboxId, + userid_t userId, uid_t uid, fail_fn_t fail_fn) { for (auto& label : volumeLabels) { std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str()); std::string mntTarget = StringPrintf("/storage/%s", label.c_str()); @@ -655,11 +680,26 @@ static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames, StringAppendF(&mntTarget, "/%d", userId); } + if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) < 0) { + ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno)); + continue; + } + + // Create /mnt/runtime/write/emulated/0/Android/{data,media,obb,sandbox} + createPkgSpecificDirRoots(mntSource, true, 0700, AID_ROOT, AID_ROOT, fail_fn); + + std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s", + mntSource.c_str(), sandboxId.c_str()); + CreateDir(sandboxSource, 0755, uid, uid, fail_fn); + BindMount(sandboxSource, mntTarget, fail_fn); + + // Create /storage/emulated/0/Android/{data,media,obb} + createPkgSpecificDirRoots(mntTarget, false, 0755, uid, uid, fail_fn); for (auto& package : packageNames) { - MountPkgSpecificDir(mntSource, mntTarget, package, "data", fail_fn); - MountPkgSpecificDir(mntSource, mntTarget, package, "media", fail_fn); + MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn); + MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn); if (!mountAllObbs) { - MountPkgSpecificDir(mntSource, mntTarget, package, "obb", fail_fn); + MountPkgSpecificDir(mntSource, mntTarget, package, uid, "obb", fail_fn); } } @@ -676,7 +716,8 @@ static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames, static void MountEmulatedStorage(uid_t uid, jint mount_mode, bool force_mount_namespace, const std::string& package_name, const std::vector<std::string>& packages_for_uid, - const std::vector<std::string>& visible_vol_ids, fail_fn_t fail_fn) { + const std::vector<std::string>& visible_vol_ids, const std::string& sandbox_id, + fail_fn_t fail_fn) { // See storage config details at http://source.android.com/tech/storage/ String8 storageSource; @@ -727,7 +768,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, strerror(errno))); } } else { - if (package_name.empty()) { + if (package_name.empty() || sandbox_id.empty()) { return; } @@ -773,7 +814,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, // care of by vold later. if (sandboxAlreadyCreated) { PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids, - mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, fail_fn); + mount_mode == MOUNT_EXTERNAL_INSTALLER, sandbox_id, user_id, uid, fail_fn); } } } else { @@ -1110,7 +1151,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, bool is_child_zygote, jstring managed_instruction_set, jstring managed_app_data_dir, jstring managed_package_name, jobjectArray managed_pacakges_for_uid, - jobjectArray managed_visible_vol_ids) { + jobjectArray managed_visible_vol_ids, jstring managed_sandbox_id) { const char* process_name = is_system_server ? "system_server" : "zygote"; auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1); auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); @@ -1120,6 +1161,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, auto instruction_set = extract_fn(managed_instruction_set); auto app_data_dir = extract_fn(managed_app_data_dir); auto package_name = extract_fn(managed_package_name); + auto sandbox_id = extract_fn(managed_sandbox_id); // Keep capabilities across UID change, unless we're staying root. if (uid != 0) { @@ -1162,7 +1204,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, value_or(std::vector<std::string>()); MountEmulatedStorage(uid, mount_external, use_native_bridge, package_name.value(), - packages_for_uid, visible_vol_ids, fail_fn); + packages_for_uid, visible_vol_ids, sandbox_id.value_or(""), fail_fn); // If this zygote isn't root, it won't be able to create a process group, // since the directory is owned by root. @@ -1462,7 +1504,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( jint mount_external, jstring se_info, jstring nice_name, jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jstring package_name, - jobjectArray packages_for_uid, jobjectArray visible_vol_ids) { + jobjectArray packages_for_uid, jobjectArray visible_vol_ids, jstring sandbox_id) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); if (UNLIKELY(managed_fds_to_close == nullptr)) { @@ -1494,7 +1536,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( capabilities, capabilities, mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, - package_name, packages_for_uid, visible_vol_ids); + package_name, packages_for_uid, visible_vol_ids, sandbox_id); } return pid; } @@ -1520,7 +1562,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true, - false, nullptr, nullptr, nullptr, nullptr, nullptr); + false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } else if (pid > 0) { // The zygote process checks whether the child process has died or not. ALOGI("System server process %d has been created", pid); @@ -1674,14 +1716,15 @@ static void com_android_internal_os_Zygote_nativeSpecializeBlastula( jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, - jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids) { + jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids, + jstring sandbox_id) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities, mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, - package_name, packages_for_uid, visible_vol_ids); + package_name, packages_for_uid, visible_vol_ids, sandbox_id); } /** @@ -1772,7 +1815,7 @@ static const JNINativeMethod gMethods[] = { { "nativeSecurityInit", "()V", (void *) com_android_internal_os_Zygote_nativeSecurityInit }, { "nativeForkAndSpecialize", - "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)I", + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I", (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize }, { "nativeForkSystemServer", "(II[II[[IJJ)I", (void *) com_android_internal_os_Zygote_nativeForkSystemServer }, @@ -1787,7 +1830,7 @@ static const JNINativeMethod gMethods[] = { { "nativeForkBlastula", "(II[I)I", (void *) com_android_internal_os_Zygote_nativeForkBlastula }, { "nativeSpecializeBlastula", - "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V", (void *) com_android_internal_os_Zygote_nativeSpecializeBlastula }, { "nativeGetSocketFDs", "(Z)V", (void *) com_android_internal_os_Zygote_nativeGetSocketFDs }, diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h index 984e942207c1..3f7c00c9ff01 100644 --- a/core/jni/include/android_runtime/android_view_Surface.h +++ b/core/jni/include/android_runtime/android_view_Surface.h @@ -55,10 +55,11 @@ enum class PublicFormat { DEPTH_POINT_CLOUD = 0x101, RAW_DEPTH = 0x1002, // @hide YV12 = 0x32315659, - Y8 = 0x20203859, // @hide + Y8 = 0x20203859, Y16 = 0x20363159, // @hide DEPTH16 = 0x44363159, DEPTH_JPEG = 0x69656963, + HEIC = 0x48454946, }; /* Gets the underlying ANativeWindow for a Surface. */ diff --git a/core/jni/runtime_native_boot-flags-test.sh b/core/jni/runtime_native_boot-flags-test.sh new file mode 100755 index 000000000000..66e18bb19c44 --- /dev/null +++ b/core/jni/runtime_native_boot-flags-test.sh @@ -0,0 +1,244 @@ +#!/bin/bash + +# 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. +# + +# Test Android Runtime (Boot) device configuration flags (living in namespace +# `runtime_native_boot`). + +me=$(basename $0) + +# Namespace containing the tested flag. +namespace=runtime_native_boot +# Default set of checked zygote processes. +zygotes= + +# Status of whole test script. +exit_status=0 + +function say { + echo "$me: $*" +} + +function banner { + local separator=$(echo "$@" | sed s/./=/g ) + say "$separator" + say "$@" + say "$separator" +} + +function fail { + say "FAILED: $@" + exit_status=1 +} + +function reboot_and_wait_for_device { + say "Rebooting device..." + adb reboot + adb wait-for-device >/dev/null + # Wait until the device has finished booting. Give the device 60 iterations + # (~60 seconds) to try and finish booting before declaring defeat. + local niters=60 + for i in $(seq $niters); do + [[ $(adb shell getprop sys.boot_completed) -eq 1 ]] && return 0 + sleep 1 + done + fail "Device did not finish booting before timeout (~$niters seconds)" +} + +# check_device_config_flag CONTEXT FLAG VALUE +# ------------------------------------------- +# Check that the device configuration flag FLAG is set to VALUE. Use CONTEXT in +# logging. +function check_device_config_flag { + local context=$1 + local flag=$2 + local value=$3 + + say "[$context] Check that the device configuration flag is set..." + local flag_value=$(adb shell device_config get "$namespace" "$flag") + [[ "$flag_value" = "$value" ]] \ + || fail "Device configuration flag \`$flag\` set to \`$flag_value\` (expected \`$value\`)" +} + +# check_no_device_config_flag CONTEXT FLAG +# ---------------------------------------- +# Check that the device configuration flag FLAG is not set. Use CONTEXT in +# logging. +function check_no_device_config_flag { + local context=$1 + local flag=$2 + + say "[$context] Check that the device configuration flag is not set..." + local flag_value=$(adb shell device_config get "$namespace" "$flag") + [[ "$flag_value" = null ]] \ + || fail "Device configuration flag \`$flag\` set to \`$flag_value\` (expected `null`)" +} + +# get_system_property PROP +# ------------------------ +# Get system property PROP associated with a device configuration flag. +function get_system_property { + local prop=$1 + + # Note that we need to be root to read that system property. + adb root >/dev/null + local prop_value=$(adb shell getprop "$prop") + adb unroot >/dev/null + echo "$prop_value" +} + +# check_system_property CONTEXT PROP VALUE +# ---------------------------------------- +# Check that the system property PROP associated with a device configuration +# flag is set to VALUE. Use CONTEXT in logging. +function check_system_property { + local context=$1 + local prop=$2 + local value=$3 + + say "[$context] Check that the persistent system property is set..." + local prop_value=$(get_system_property "$prop") + [[ "$prop_value" = "$value" ]] \ + || fail "System property \`$prop\` set to \`$prop_value\` (expected \`$value\`)" +} + +# check_no_system_property CONTEXT PROP +# ------------------------------------- +# Check that the system property PROP associated with a device configuration +# flag is not set. Use CONTEXT in logging. +function check_no_system_property { + local context=$1 + local prop=$2 + + say "[$context] Check that the persistent system property is not set..." + local prop_value=$(get_system_property "$prop") + [[ -z "$prop_value" ]] \ + || fail "System property \`$prop\` set to \`$prop_value\` (expected unset property)" +} + +# find_zygote_runtime_option ZYGOTE RUNTIME_OPTION +# ------------------------------------------------ +# Return whether ZYGOTE is passed RUNTIME_OPTION. +function find_zygote_runtime_option { + local zygote=$1 + local runtime_option=$2 + + adb logcat -d -s "$zygote" | grep -q -e "option\[[0-9]\+\]=$runtime_option" +} + +# check_zygote_gc_runtime_option CONTEXT VALUE +# -------------------------------------------- +# Check that all zygote processes are passed device configuration flag VALUE as +# GC runtime option. Use CONTEXT in logging. +function check_zygote_gc_runtime_option { + local context=$1 + local value=$2 + + say \ + "[$context] Check that all zygote processes are passed the flag value as a GC runtime option..." + local runtime_option="-Xgc:$value" + for zygote in $zygotes; do + find_zygote_runtime_option "$zygote" "$runtime_option"\ + || fail "Found no \`$runtime_option\` among runtime options passed to \`$zygote\`" + done +} + +# check_no_zygote_gc_runtime_option CONTEXT VALUE +# ----------------------------------------------- +# Check that no zygote process is passed device configuration flag VALUE as GC +# runtime option. Use CONTEXT in logging. +function check_no_zygote_gc_runtime_option { + local context=$1 + local value=$2 + + say "[$context] Check no zygote process is passed the flag value as a GC runtime option..." + local runtime_option="-Xgc:$value" + for zygote in $zygotes; do + find_zygote_runtime_option "$zygote" "$runtime_option"\ + && fail "Found \`$runtime_option\` among runtime options passed to \`$zygote\`" + done +} + +# test_android_runtime_flag FLAG VALUE +# ------------------------------------ +# Test device configuration FLAG with VALUE. +function test_android_runtime_flag { + local flag=$1 + local value=$2 + + # Persistent system property (set after a reboot) associated with the device + # configuration flag. + local prop="persist.device_config.$namespace.$flag" + + banner "Testing \`$flag\` value \`$value\`." + + say "Setting device configuration flag..." + adb shell device_config put "$namespace" "$flag" "$value" + # Give some time to the device to digest this change before rebooting. + sleep 3 + + # Check that both the device configuration flag and the associated system + # property are set, but that the zygote hasn't had the flag passed to it as a + # GC runtime option (as we haven't rebooted yet). + local context="Flag set, before reboot" + check_device_config_flag "$context" "$flag" "$value" + check_system_property "$context" "$prop" "$value" + check_no_zygote_gc_runtime_option "$context" "$value" + + # Reboot device for the flag value to take effect. + reboot_and_wait_for_device + context="Flag set, after 1st reboot" + check_device_config_flag "$context" "$flag" "$value" + check_system_property "$context" "$prop" "$value" + check_zygote_gc_runtime_option "$context" "$value" + + # Reboot device a second time and check that the state has persisted. + reboot_and_wait_for_device + context="Flag set, after 2nd reboot" + check_device_config_flag "$context" "$flag" "$value" + check_system_property "$context" "$prop" "$value" + check_zygote_gc_runtime_option "$context" "$value" + + say "Unsetting device configuration flag..." + adb shell device_config delete "$namespace" "$flag" >/dev/null + # Give some time to the device to digest this change before rebooting. + sleep 3 + + # Reboot and check that the device is back to its default state. + reboot_and_wait_for_device + context="Flag unset, after 3rd reboot" + check_no_device_config_flag "$context" "$flag" + check_no_system_property "$context" "$prop" + check_no_zygote_gc_runtime_option "$context" "$value" +} + +# Enumerate Zygote processes. +case $(adb shell getprop ro.zygote) in + (zygote32) zygotes="zygote";; + (zygote64) zygotes="zygote64";; + (zygote32_64|zygote64_32) zygotes="zygote zygote64";; +esac + +# Test "gctype" flag values. +test_android_runtime_flag gctype nogenerational_cc +test_android_runtime_flag gctype generational_cc + +if [[ "$exit_status" -eq 0 ]]; then + banner "All tests passed." +else + banner "Test(s) failed." +fi +exit $exit_status diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto index 5b5c9c28b4a0..b4f3d1ea5ae4 100644 --- a/core/proto/android/bluetooth/enums.proto +++ b/core/proto/android/bluetooth/enums.proto @@ -132,3 +132,9 @@ enum SocketConnectionstateEnum { // This socket is closed SOCKET_CONNECTION_STATE_DISCONNECTED = 5; } + +enum SocketRoleEnum { + SOCKET_ROLE_UNKNOWN = 0; + SOCKET_ROLE_LISTEN = 1; + SOCKET_ROLE_CONNECTION = 2; +} diff --git a/core/proto/android/hardware/biometrics/enums.proto b/core/proto/android/hardware/biometrics/enums.proto index 91f2acbbaf03..973e3e65cf1a 100644 --- a/core/proto/android/hardware/biometrics/enums.proto +++ b/core/proto/android/hardware/biometrics/enums.proto @@ -43,4 +43,16 @@ enum ActionEnum { ACTION_AUTHENTICATE = 2; ACTION_ENUMERATE = 3; ACTION_REMOVE = 4; +} + +enum IssueEnum { + ISSUE_UNKNOWN = 0; + // When a biometric HAL has crashed. + ISSUE_HAL_DEATH = 1; + // When Android Framework has a template that doesn't exist in the HAL. The framework + // is expected to remove its template to stay in sync with the HAL. + ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK = 2; + // When the HAL has a template that doesn't exist in Android Framework. The framework + // is expected to notify the HAL to remove this template to stay in sync with the framework. + ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL = 3; }
\ No newline at end of file diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index a160451ecfed..d5776534bb90 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -259,6 +259,8 @@ message GlobalSettingsProto { optional SettingProto app = 1; // Whether views are allowed to save their attribute data. optional SettingProto view_attributes = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Which application package is allowed to save view attribute data. + optional SettingProto view_attributes_application_package = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Debug debug = 37; diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index f3733fdb3810..6360a5f91013 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -321,6 +321,7 @@ message SecureSettingsProto { optional SettingProto badging = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto show_note_about_notification_hiding = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto in_call_notification_enabled = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bubbles = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Notification notification = 41; diff --git a/core/proto/android/wifi/enums.proto b/core/proto/android/wifi/enums.proto new file mode 100644 index 000000000000..315c5792c1de --- /dev/null +++ b/core/proto/android/wifi/enums.proto @@ -0,0 +1,50 @@ +/* + * 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. + */ + +syntax = "proto2"; +package android.net.wifi; + +option java_outer_classname = "WifiProtoEnums"; +option java_multiple_files = true; + +/** + * Wifi Lock modes, primarily used in + * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiLockManager.java. + */ +enum WifiModeEnum { + /** + * Deprecated. + * Wi-Fi will be kept active, and will behave normally. + */ + WIFI_MODE_FULL = 1 [deprecated=true]; + + /** + * Deprecated. + * Wi-Fi will be kept active, but the only operation that will be supported is initiation of + * scans, and the subsequent reporting of scan results. + */ + WIFI_MODE_SCAN_ONLY = 2 [deprecated=true]; + + /** + * Wi-Fi will not go to power save. + */ + WIFI_MODE_FULL_HIGH_PERF = 3; + + /** + * Wi-Fi will operate with a priority to achieve low latency. + */ + WIFI_MODE_FULL_LOW_LATENCY = 4; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c7b528c924dc..34ec92e68511 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -662,8 +662,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readContacts" android:description="@string/permdesc_readContacts" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to write the user's contacts data. <p>Protection level: dangerous @@ -694,8 +693,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCalendar" android:description="@string/permdesc_readCalendar" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to write the user's calendar data. <p>Protection level: dangerous @@ -736,8 +734,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveSms" android:description="@string/permdesc_receiveSms" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to read SMS messages. <p>Protection level: dangerous @@ -746,8 +743,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readSms" android:description="@string/permdesc_readSms" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to receive WAP push messages. <p>Protection level: dangerous @@ -756,8 +752,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveWapPush" android:description="@string/permdesc_receiveWapPush" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to monitor incoming MMS messages. <p>Protection level: dangerous @@ -766,8 +761,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveMms" android:description="@string/permdesc_receiveMms" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast messages and to register a content observer to get notifications when @@ -785,8 +779,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCellBroadcasts" android:description="@string/permdesc_readCellBroadcasts" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- ====================================================================== --> <!-- Permissions for accessing external storage --> @@ -867,8 +860,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_audioRead" android:description="@string/permdesc_audioRead" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Runtime permission controlling access to the user's shared visual media collection, including images and videos. --> @@ -884,16 +876,14 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_imagesRead" android:description="@string/permdesc_imagesRead" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to read the user's shared video collection. --> <permission android:name="android.permission.READ_MEDIA_VIDEO" android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_videoRead" android:description="@string/permdesc_videoRead" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to access any geographic locations persisted in the user's shared collection. --> @@ -901,8 +891,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_mediaLocation" android:description="@string/permdesc_mediaLocation" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- @hide @SystemApi @TestApi Allows an application to modify OBB files visible to other apps. --> @@ -934,8 +923,7 @@ android:label="@string/permlab_accessFineLocation" android:description="@string/permdesc_accessFineLocation" android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- Allows an app to access approximate location. Alternatively, you might want {@link #ACCESS_FINE_LOCATION}. @@ -946,8 +934,7 @@ android:label="@string/permlab_accessCoarseLocation" android:description="@string/permdesc_accessCoarseLocation" android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- Allows an app to access location in the background. If you are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}. @@ -959,8 +946,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_accessBackgroundLocation" android:description="@string/permdesc_accessBackgroundLocation" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- ====================================================================== --> <!-- Permissions for accessing the call log --> @@ -1001,8 +987,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCallLog" android:description="@string/permdesc_readCallLog" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an application to write (but not read) the user's call log data. @@ -1032,8 +1017,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_processOutgoingCalls" android:description="@string/permdesc_processOutgoingCalls" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- ====================================================================== --> <!-- Permissions for accessing the device telephony --> @@ -1065,8 +1049,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readPhoneState" android:description="@string/permdesc_readPhoneState" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities granted by {@link #READ_PHONE_STATE} but is exposed to instant applications. @@ -1075,8 +1058,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readPhoneNumbers" android:description="@string/permdesc_readPhoneNumbers" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call. @@ -1178,8 +1160,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_recordAudio" android:description="@string/permdesc_recordAudio" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- ====================================================================== --> <!-- Permissions for activity recognition --> @@ -1202,8 +1183,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_activityRecognition" android:description="@string/permdesc_activityRecognition" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- ====================================================================== --> <!-- Permissions for accessing the UCE Service --> @@ -1252,8 +1232,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_camera" android:description="@string/permdesc_camera" - android:protectionLevel="dangerous|instant" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous|instant" /> <!-- ====================================================================== --> @@ -1277,8 +1256,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_bodySensors" android:description="@string/permdesc_bodySensors" - android:protectionLevel="dangerous" - android:usageInfoRequired="true" /> + android:protectionLevel="dangerous" /> <!-- Allows an app to use fingerprint hardware. <p>Protection level: normal @@ -1780,8 +1758,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:protectionLevel="dangerous" android:description="@string/permdesc_getAccounts" - android:label="@string/permlab_getAccounts" - android:usageInfoRequired="true" /> + android:label="@string/permlab_getAccounts" /> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <!-- Allows applications to call into AccountAuthenticators. @@ -2290,7 +2267,7 @@ <!-- Allows an application to start activities from background @hide --> <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" - android:protectionLevel="signature|privileged|vendorPrivileged|oem" /> + android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" /> <!-- @SystemApi Must be required by activities that handle the intent action {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that @@ -4411,8 +4388,8 @@ <permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" android:protectionLevel="signature|privileged" /> - <!-- A subclass of {@link android.app.SmsAppService} must be protected with this permission. --> - <permission android:name="android.permission.BIND_SMS_APP_SERVICE" + <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission. --> + <permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE" android:protectionLevel="signature" /> <!-- @hide Permission that allows configuring appops. diff --git a/core/res/res/drawable/bottomsheet_background.xml b/core/res/res/drawable/bottomsheet_background.xml new file mode 100644 index 000000000000..bc32ba6e3896 --- /dev/null +++ b/core/res/res/drawable/bottomsheet_background.xml @@ -0,0 +1,22 @@ +<?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. +--> + +<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android"> + <corners + android:topLeftRadius="?attr/dialogCornerRadius" + android:topRightRadius="?attr/dialogCornerRadius" /> + <solid android:color="?attr/colorBackgroundFloating" /> +</shape> diff --git a/core/res/res/drawable/ic_action_open.xml b/core/res/res/drawable/ic_action_open.xml new file mode 100644 index 000000000000..3d3d36ece0af --- /dev/null +++ b/core/res/res/drawable/ic_action_open.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF737373" + android:pathData="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_drag_handle.xml b/core/res/res/drawable/ic_drag_handle.xml new file mode 100644 index 000000000000..67ab84d4080d --- /dev/null +++ b/core/res/res/drawable/ic_drag_handle.xml @@ -0,0 +1,23 @@ +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M20.0,9.0L4.0,9.0l0.0,2.0l16.0,0.0L20.0,9.0zM4.0,15.0l16.0,0.0l0.0,-2.0L4.0,13.0l0.0,2.0z"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index f78466168e13..14a5310a4ff2 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -25,11 +25,24 @@ android:maxCollapsedHeightSmall="56dp" android:id="@id/contentPanel"> + <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" - android:background="?attr/colorBackgroundFloating"> + android:background="@drawable/bottomsheet_background"> + + <ImageView + android:id="@+id/drag" + android:layout_width="48dp" + android:layout_height="wrap_content" + android:src="@drawable/ic_drag_handle" + android:clickable="true" + android:paddingTop="@dimen/chooser_edge_margin_normal" + android:tint="?android:attr/textColorSecondary" + android:layout_centerHorizontal="true" + android:layout_alignParentTop="true" /> + <TextView android:id="@+id/profile_button" android:layout_width="wrap_content" android:layout_height="48dp" @@ -41,7 +54,7 @@ android:textAppearance="?attr/textAppearanceButton" android:textColor="?attr/colorAccent" android:gravity="center_vertical" - android:layout_alignParentTop="true" + android:layout_below="@id/drag" android:layout_alignParentRight="true" android:singleLine="true"/> @@ -207,7 +220,6 @@ android:clipToPadding="false" android:scrollbarStyle="outsideOverlay" android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" android:listSelector="@color/transparent" android:divider="@null" android:scrollIndicators="top" @@ -219,7 +231,7 @@ android:layout_alwaysShow="true" android:background="?attr/colorBackgroundFloating" android:text="@string/noApplications" - android:padding="32dp" + android:padding="@dimen/chooser_edge_margin_normal" android:gravity="center" android:visibility="gone"/> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index fa3a549463f5..46e14b41960c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2426,6 +2426,8 @@ </attr> <attr name="__removed3" /> + <attr name="__removed4" /> + <attr name="__removed5" /> <!-- Describes the content of a view so that a autofill service can fill in the appropriate data. Multiple hints can be combined in a comma separated list or an array of strings diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 759fc12dadae..53cae638db80 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -164,6 +164,15 @@ provider.--> <attr name="grantUriPermissions" format="boolean" /> + <!-- If true, the system will always create URI permission grants + in the cases where {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} + or {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} would apply. + This is useful for a content provider that dynamically enforces permissions + on calls in to the provider, instead of through the manifest: the system + needs to know that it should always apply permission grants, even if it + looks like the target of the grant would already have access to the URI. --> + <attr name="forceUriPermissions" format="boolean" /> + <!-- Characterizes the potential risk implied in a permission and indicates the procedure the system should follow when determining whether to grant the permission to an application requesting it. {@link @@ -1680,10 +1689,6 @@ <attr name="request" /> <attr name="protectionLevel" /> <attr name="permissionFlags" /> - <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage - attributes in their {@code uses-permission} elements or the permission will not be - granted. --> - <attr name="usageInfoRequired" format="boolean" /> </declare-styleable> <!-- The <code>permission-group</code> tag declares a logical grouping of @@ -1783,81 +1788,6 @@ requested. If it does support the feature, it will be as if the manifest didn't request it at all. --> <attr name="requiredNotFeature" format="string" /> - - <!-- Specify if the app uploads data, or derived data, guarded by this permission. - - If the permission is defined with {@link android.R.attr#usageInfoRequired} - {@code true} this <em>must</em> be specified by apps that target Android Q or the - permission will not be granted, it will be as if the manifest didn't request it at all. - --> - <attr name="dataSentOffDevice"> - <!-- The application may send data, or derived data, guarded by this permission off of the - device. --> - <enum name="yes" value="1" /> - <!-- The application may send data, or derived data, guarded by this permission off of the - device, however it will only do so when explicitly triggered by a user action. --> - <enum name="userTriggered" value="2" /> - <!-- The application does not send data, or derived data, guarded by this permission off - of the device. --> - <enum name="no" value="3" /> - </attr> - - <!-- Specify if the application or its related off-device services provide data, - or derived data, guarded by this permission to third parties outside of the developer's - organization that do not qualify as data processors. - - If the permission is defined with {@link android.R.attr#usageInfoRequired} - {@code true} this <em>must</em> be specified by apps that target Android Q or the - permission will not be granted, it will be as if the manifest didn't request it at all. - --> - <attr name="dataSharedWithThirdParty"> - <!-- The application or its services may provide data, or derived data, guarded by this - permission to third party organizations. --> - <enum name="yes" value="1" /> - <!-- The application or its services may provide data, or derived data, guarded by this - permission to third party organizations, however it will only do so when explicitly - triggered by a user action. --> - <enum name="userTriggered" value="2" /> - <!-- The application or its services does not provide data, or derived data, guarded by - this permission to third party organizations. --> - <enum name="no" value="3" /> - </attr> - - <!-- Specify if the application or its related off-device services use data, - or derived data, guarded by this permission for monetization purposes. - - For example, if the data is sold to another party or used for targeting advertisements - this must be set to {@code yes}. - - If the permission is defined with {@link android.R.attr#usageInfoRequired} - {@code true} this <em>must</em> be specified by apps that target Android Q or the - permission will not be granted, it will be as if the manifest didn't request it at all. - --> - <attr name="dataUsedForMonetization"> - <!-- The application or its services may use data, or derived data, guarded by this - permission for monetization purposes. --> - <enum name="yes" value="1" /> - <!-- The application or its services may use data, or derived data, guarded by this - permission for monetization purposes, however it will only do so when explicity - triggered by a user action. --> - <enum name="userTriggered" value="2" /> - <!-- The application or its services does not use data, or derived data, guarded by - this permission for monetization purposes. --> - <enum name="no" value="3" /> - </attr> - - <!-- Specify how long the application or its related off-device services store - data, or derived data, guarded by this permission. - - This can be one of "notRetained", "userSelected", "unlimited", or a number - representing the number of weeks the data is retained. - - If the permission is defined with {@link android.R.attr#usageInfoRequired} - {@code true} this <em>must</em> be specified by apps that target Android Q or the - permission will not be granted, it will be as if the manifest didn't request it at all. - --> - <attr name="dataRetentionTime" format="string" /> - </declare-styleable> <!-- The <code>uses-configuration</code> tag specifies @@ -2199,6 +2129,7 @@ <attr name="readPermission" /> <attr name="writePermission" /> <attr name="grantUriPermissions" /> + <attr name="forceUriPermissions" /> <attr name="permission" /> <attr name="multiprocess" /> <attr name="initOrder" /> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 16c074484e2c..02fae4a2c1b3 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -199,8 +199,6 @@ <color name="Red_700">#ffc53929</color> <color name="Red_800">#ffb93221</color> - <color name="chooser_service_row_background_color">#fff5f5f5</color> - <!-- Status bar color for semi transparent mode. --> <color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9e34441cc794..130f6291b516 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -963,7 +963,7 @@ <bool name="config_nightDisplayAvailable">@bool/config_setColorTransformAccelerated</bool> <!-- Default mode to control how Night display is automatically activated. - One of the following values (see ColorDisplayController.java): + One of the following values (see ColorDisplayManager.java): 0 - AUTO_MODE_DISABLED 1 - AUTO_MODE_CUSTOM_TIME 2 - AUTO_MODE_TWILIGHT @@ -1052,7 +1052,7 @@ </string-array> - <!-- Indicate available ColorDisplayController.COLOR_MODE_xxx. --> + <!-- Indicate available ColorDisplayManager.COLOR_MODE_xxx. --> <integer-array name="config_availableColorModes"> <!-- Example: <item>0</item> @@ -1594,7 +1594,7 @@ <integer-array name="config_screenBrighteningThresholds"> <item>100</item> </integer-array> - + <!-- Array of hysteresis constraint values for darkening, represented as tenths of a percent. The length of this array is assumed to be one greater than config_screenThresholdLevels. The darkening threshold is calculated as @@ -1921,8 +1921,6 @@ cell broadcasting sms, and MMS. --> <bool name="config_sms_capable">true</bool> - <!-- TODO: STOPSHIP(b/110557011): Remove this from framework and overlays as we use - config_defaultRoleHolders now. --> <!-- Default SMS Application. This will be the default SMS application when the phone first boots. The user can then change the default app to one of their choosing. @@ -1930,15 +1928,33 @@ application is desired. If this string is empty or the specified package does not exist, then - the platform will search for an SMS app and use that (if there is one)--> + the platform will search for an SMS app and use that (if there is one) + + Note: This config is deprecated, please use config_defaultSms instead. --> <string name="default_sms_application" translatable="false">com.android.messaging</string> - <!-- Default role holders. This will be an array of roles and package names of their default - holders, with each item in the format of "ROLE_NAME: PACKAGE_NAME_1, PACKAGE_NAME_2". --> - <string-array name="config_defaultRoleHolders" translatable="false"> - <item>android.app.role.SMS: com.android.messaging</item> - <item>android.app.role.DIALER: com.android.phone</item> - </string-array> + <!-- Default web browser. This is the package name of the application that will + be the default browser when the device first boots. Afterwards the user + can select whatever browser app they wish to use as the default. + + If this string is empty or the specified package does not exist, then + the behavior will be as though no app was named as an explicit default. + + Note: This config is deprecated, please use config_defaultBrowser instead. --> + <string name="default_browser" translatable="false"></string> + + <!-- The name of the package that will hold the assistant role by default. --> + <string name="config_defaultAssistant" translatable="false" /> + <!-- The name of the package that will hold the browser role by default. --> + <string name="config_defaultBrowser" translatable="false">@string/default_browser</string> + <!-- The name of the package that will hold the dialer role by default. --> + <string name="config_defaultDialer" translatable="false">com.android.phone</string> + <!-- The name of the package that will hold the SMS role by default. --> + <string name="config_defaultSms" translatable="false">@string/default_sms_application</string> + <!-- The name of the package that will hold the music role by default. --> + <string name="config_defaultMusic" translatable="false">com.android.music</string> + <!-- The name of the package that will hold the gallery role by default. --> + <string name="config_defaultGallery" translatable="false">com.android.gallery3d</string> <!-- Enable/disable default bluetooth profiles: HSP_AG, ObexObjectPush, Audio, NAP --> @@ -2632,6 +2648,7 @@ extractor must come first --> <item>com.android.server.notification.NotificationChannelExtractor</item> <item>com.android.server.notification.NotificationAdjustmentExtractor</item> + <item>com.android.server.notification.BubbleExtractor</item> <!-- depends on AdjustmentExtractor--> <item>com.android.server.notification.ValidateNotificationPeople</item> <item>com.android.server.notification.PriorityExtractor</item> @@ -3424,8 +3441,10 @@ <!-- Flag indicates that whether non-system apps can be installed on internal storage. --> <bool name="config_allow3rdPartyAppOnInternal">true</bool> - <!-- Package name of the default cell broadcast receiver --> - <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string> + <!-- Package names of the default cell broadcast receivers --> + <string-array name="config_defaultCellBroadcastReceiverPkgs" translatable="false"> + <item>com.android.cellbroadcastreceiver</item> + </string-array> <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. --> <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index bbe3ff995ac3..ce7995a93f7f 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -193,4 +193,7 @@ <!-- A tag used to save the notification action object --> <item type="id" name="notification_action_index_tag" /> + + <!-- A tag used to save the index where the custom view is stored --> + <item type="id" name="notification_custom_view_index_tag" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e7d8102ff83e..d2c3b40cc3b6 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2925,11 +2925,11 @@ <public name="importantForContentCapture" /> <public name="supportsMultipleDisplays" /> <public name="useAppZygote" /> - <public name="usageInfoRequired" /> - <public name="dataSentOffDevice" /> - <public name="dataSharedWithThirdParty" /> - <public name="dataUsedForMonetization" /> - <public name="dataRetentionTime" /> + <public name="__removed1" /> + <public name="__removed2" /> + <public name="__removed3" /> + <public name="__removed4" /> + <public name="__removed5" /> <public name="selectionDividerHeight" /> <public name="foregroundServiceType" /> <public name="hasFragileUserData" /> @@ -2938,6 +2938,7 @@ <public name="inheritShowWhenLocked" /> <public name="zygotePreloadName" /> <public name="useEmbeddedDex" /> + <public name="forceUriPermissions" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> @@ -2970,6 +2971,18 @@ <public name="config_feedbackIntentExtraKey" /> <!-- @hide @SystemApi --> <public name="config_feedbackIntentNameKey" /> + <!-- @hide @SystemApi @TestApi --> + <public name="config_defaultAssistant" /> + <!-- @hide @SystemApi --> + <public name="config_defaultBrowser" /> + <!-- @hide @SystemApi @TestApi --> + <public name="config_defaultDialer" /> + <!-- @hide @SystemApi --> + <public name="config_defaultSms" /> + <!-- @hide @SystemApi --> + <public name="config_defaultMusic" /> + <!-- @hide @SystemApi --> + <public name="config_defaultGallery" /> </public-group> <public-group type="bool" first-id="0x01110000"> @@ -2989,11 +3002,6 @@ <public name="system_notification_accent_color" /> </public-group> - <public-group type="array" first-id="0x01070006"> - <!-- @hide @TestApi @SystemApi --> - <public name="config_defaultRoleHolders" /> - </public-group> - <!-- =============================================================== DO NOT ADD UN-GROUPED ITEMS HERE diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index fadb28f05ef7..6d5bd4ba1645 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1471,6 +1471,8 @@ <string name="biometric_not_recognized">Not recognized</string> <!-- Message shown when biometric authentication has been canceled [CHAR LIMIT=50] --> <string name="biometric_error_canceled">Authentication canceled</string> + <!-- Message returned to applications if BiometricPrompt setAllowDeviceCredentials is enabled but no pin, pattern, or password is set. [CHAR LIMIT=NONE] --> + <string name="biometric_error_device_not_secured">No pin, pattern, or password set</string> <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> <string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string> @@ -1512,7 +1514,7 @@ <!-- Generic error message shown when the user has no enrolled fingerprints --> <string name="fingerprint_error_no_fingerprints">No fingerprints enrolled.</string> <!-- Generic error message shown when the app requests fingerprint authentication on a device without a sensor --> - <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor</string> + <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor.</string> <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> @@ -1555,8 +1557,22 @@ <string name="face_acquired_poor_gaze">Please look at the sensor.</string> <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] --> <string name="face_acquired_not_detected">No face detected.</string> - <!-- Message shown during face acquisition when the face is not kept steady infront of device [CHAR LIMIT=50] --> - <string name="face_acquired_not_steady">Keep face steady infront of device.</string> + <!-- Message shown during face acquisition when the device is not steady [CHAR LIMIT=50] --> + <string name="face_acquired_too_much_motion">Too much motion.</string> + <!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] --> + <string name="face_acquired_recalibrate">Please re-enroll your face.</string> + <!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] --> + <string name="face_acquired_too_different">Different face detected.</string> + <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> + <string name="face_acquired_too_similar">Too similar, please change your pose.</string> + <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> + <string name="face_acquired_pan_too_extreme">Please look more directly at the camera.</string> + <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> + <string name="face_acquired_tilt_too_extreme">Please look more directly at the camera.</string> + <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> + <string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string> + <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> + <string name="face_acquired_obscured">Please uncover your face.</string> <!-- Array containing custom messages shown during face acquisition from vendor. Vendor is expected to add and translate these strings --> <string-array name="face_acquired_vendor"> </string-array> @@ -1580,7 +1596,7 @@ <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] --> <string name="face_error_not_enrolled">No face enrolled.</string> <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] --> - <string name="face_error_hw_not_present">This device does not have a face authentication sensor</string> + <string name="face_error_hw_not_present">This device does not have a face authentication sensor.</string> <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] --> <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6d4b04c5e66a..8e251fd4ea6f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1034,6 +1034,7 @@ <java-symbol type="string" name="sipAddressTypeOther" /> <java-symbol type="string" name="sipAddressTypeWork" /> <java-symbol type="string" name="default_sms_application" /> + <java-symbol type="string" name="default_browser" /> <java-symbol type="string" name="sms_control_message" /> <java-symbol type="string" name="sms_control_title" /> <java-symbol type="string" name="sms_control_no" /> @@ -2472,6 +2473,7 @@ <java-symbol type="string" name="biometric_error_user_canceled" /> <java-symbol type="string" name="biometric_not_recognized" /> <java-symbol type="string" name="biometric_error_canceled" /> + <java-symbol type="string" name="biometric_error_device_not_secured" /> <!-- Fingerprint messages --> <java-symbol type="string" name="fingerprint_error_unable_to_process" /> @@ -2521,6 +2523,14 @@ <java-symbol type="string" name="face_acquired_too_left" /> <java-symbol type="string" name="face_acquired_poor_gaze" /> <java-symbol type="string" name="face_acquired_not_detected" /> + <java-symbol type="string" name="face_acquired_too_much_motion" /> + <java-symbol type="string" name="face_acquired_recalibrate" /> + <java-symbol type="string" name="face_acquired_too_different" /> + <java-symbol type="string" name="face_acquired_too_similar" /> + <java-symbol type="string" name="face_acquired_pan_too_extreme" /> + <java-symbol type="string" name="face_acquired_tilt_too_extreme" /> + <java-symbol type="string" name="face_acquired_roll_too_extreme" /> + <java-symbol type="string" name="face_acquired_obscured" /> <java-symbol type="array" name="face_acquired_vendor" /> <java-symbol type="string" name="face_name_template" /> <java-symbol type="string" name="face_authenticated_no_confirmation_required" /> @@ -2744,7 +2754,6 @@ <java-symbol type="drawable" name="scroll_indicator_material" /> <java-symbol type="layout" name="chooser_row" /> - <java-symbol type="color" name="chooser_service_row_background_color" /> <java-symbol type="id" name="target_badge" /> <java-symbol type="bool" name="config_supportDoubleTapWake" /> <java-symbol type="drawable" name="ic_perm_device_info" /> @@ -3152,7 +3161,7 @@ <java-symbol type="drawable" name="lockscreen_selected" /> <java-symbol type="string" name="notification_header_divider_symbol_with_spaces" /> - <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" /> + <java-symbol type="array" name="config_defaultCellBroadcastReceiverPkgs" /> <java-symbol type="color" name="notification_primary_text_color_light" /> <java-symbol type="color" name="notification_primary_text_color_dark" /> @@ -3586,6 +3595,7 @@ <java-symbol type="bool" name="config_useSmsAppService" /> <java-symbol type="id" name="transition_overlay_view_tag" /> + <java-symbol type="id" name="notification_custom_view_index_tag" /> <java-symbol type="dimen" name="rounded_corner_radius" /> <java-symbol type="dimen" name="rounded_corner_radius_top" /> @@ -3596,7 +3606,7 @@ <!-- For Secondary Launcher --> <java-symbol type="string" name="config_secondaryHomeComponent" /> - + <java-symbol type="string" name="dynamic_mode_notification_channel_name" /> <java-symbol type="string" name="dynamic_mode_notification_title" /> <java-symbol type="string" name="dynamic_mode_notification_summary" /> @@ -3633,4 +3643,6 @@ <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" /> + + <java-symbol type="drawable" name="ic_action_open" /> </resources> diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java index 300394d426e4..aa0e0cdae265 100644 --- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java @@ -162,14 +162,13 @@ public class PackageParserTest { } private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename, - boolean isPlatformReleased, int expectedTargetSdk, boolean forceCurrentDev) { + boolean isPlatformReleased, int expectedTargetSdk) { final String[] outError = new String[1]; final int result = PackageParser.computeTargetSdkVersion( targetSdkVersion, targetSdkCodename, isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE, - outError, - forceCurrentDev); + outError); assertEquals(result, expectedTargetSdk); @@ -185,28 +184,23 @@ public class PackageParserTest { // Do allow older release targetSdkVersion on pre-release platform. // APP: Released API 10 // DEV: Pre-release API 20 - verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION); // Do allow same release targetSdkVersion on pre-release platform. // APP: Released API 20 // DEV: Pre-release API 20 - verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION); // Do allow newer release targetSdkVersion on pre-release platform. // APP: Released API 30 // DEV: Pre-release API 20 - verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION); // Don't allow older pre-release targetSdkVersion on pre-release platform. // APP: Pre-release API 10 // DEV: Pre-release API 20 - verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1, - false /* forceCurrentDev */); - verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1); + verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1); // Do allow same pre-release targetSdkVersion on pre-release platform, @@ -214,27 +208,16 @@ public class PackageParserTest { // APP: Pre-release API 20 // DEV: Pre-release API 20 verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false, - Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */); + Build.VERSION_CODES.CUR_DEVELOPMENT); verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false, - Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */); + Build.VERSION_CODES.CUR_DEVELOPMENT); // Don't allow newer pre-release targetSdkVersion on pre-release platform. // APP: Pre-release API 30 // DEV: Pre-release API 20 - verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1, - false /* forceCurrentDev */); - verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1, - false /* forceCurrentDev */); - - - // Force newer pre-release targetSdkVersion to current pre-release platform. - // APP: Pre-release API 30 - // DEV: Pre-release API 20 - verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, - Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */); - verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, - Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */); + verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1); + verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1); } @Test @@ -242,45 +225,36 @@ public class PackageParserTest { // Do allow older release targetSdkVersion on released platform. // APP: Released API 10 // DEV: Released API 20 - verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION); // Do allow same release targetSdkVersion on released platform. // APP: Released API 20 // DEV: Released API 20 - verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION); // Do allow newer release targetSdkVersion on released platform. // APP: Released API 30 // DEV: Released API 20 - verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION); // Don't allow older pre-release targetSdkVersion on released platform. // APP: Pre-release API 10 // DEV: Released API 20 - verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1, - false /* forceCurrentDev */); - verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1); + verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1); // Don't allow same pre-release targetSdkVersion on released platform. // APP: Pre-release API 20 // DEV: Released API 20 - verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1, - false /* forceCurrentDev */); - verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1); + verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1); // Don't allow newer pre-release targetSdkVersion on released platform. // APP: Pre-release API 30 // DEV: Released API 20 - verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1, - false /* forceCurrentDev */); - verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1, - false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1); + verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1); } /** diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 2cb925aa9549..ec57f793f15f 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -210,6 +210,7 @@ public class SettingsBackupTest { Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD, Settings.Global.DEBUG_APP, Settings.Global.DEBUG_VIEW_ATTRIBUTES, + Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, Settings.Global.DEFAULT_DNS_SERVER, Settings.Global.DEFAULT_INSTALL_LOCATION, Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA, diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index da81d176de8a..80b1f9cb35ae 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -19,6 +19,7 @@ package android.view; import static android.view.ImeInsetsSourceConsumer.areEditorsSimilar; import static android.view.InsetsState.TYPE_IME; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -73,7 +74,7 @@ public class ImeInsetsSourceConsumerTest { false, new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), - rect, rect); + rect, rect, SOFT_INPUT_ADJUST_RESIZE); mImeConsumer = new ImeInsetsSourceConsumer( new InsetsState(), Transaction::new, mController); }); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 6dad6a22f7ea..731d5644504b 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -20,10 +20,16 @@ import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.Insets; @@ -73,7 +79,7 @@ public class InsetsControllerTest { false, new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), - rect, rect); + rect, rect, SOFT_INPUT_ADJUST_RESIZE); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @@ -95,6 +101,17 @@ public class InsetsControllerTest { } @Test + public void testFrameDoesntMatchDisplay() { + mController.onFrameChanged(new Rect(0, 0, 100, 100)); + mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); + WindowInsetsAnimationControlListener controlListener = + mock(WindowInsetsAnimationControlListener.class); + mController.controlWindowInsetsAnimation(0, controlListener); + verify(controlListener).onCancelled(); + verify(controlListener, never()).onReady(any(), anyInt()); + } + + @Test public void testAnimationEndState() { InsetsSourceControl[] controls = prepareControls(); InsetsSourceControl navBar = controls[0]; diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 03af67df13d0..bd036b01c0cf 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -25,6 +25,8 @@ import static android.view.InsetsState.TYPE_SIDE_BAR_2; import static android.view.InsetsState.TYPE_SIDE_BAR_3; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowInsets.Type.ime; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -37,6 +39,7 @@ import android.os.Parcel; import android.platform.test.annotations.Presubmit; import android.util.SparseIntArray; import android.view.WindowInsets.Type; +import android.view.test.InsetsModeSession; import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; @@ -53,49 +56,70 @@ public class InsetsStateTest { private InsetsState mState2 = new InsetsState(); @Test - public void testCalculateInsets() { - mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); - mState.getSource(TYPE_TOP_BAR).setVisible(true); - mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); - mState.getSource(TYPE_IME).setVisible(true); - SparseIntArray typeSideMap = new SparseIntArray(); - WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT, null, null, typeSideMap); - assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets()); - assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all())); - assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR)); - assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME)); - assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar())); - assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime())); + public void testCalculateInsets() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + SparseIntArray typeSideMap = new SparseIntArray(); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, typeSideMap); + assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets()); + assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all())); + assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR)); + assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME)); + assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar())); + assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime())); + } } @Test - public void testCalculateInsets_imeAndNav() { - mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300)); - mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); - mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300)); - mState.getSource(TYPE_IME).setVisible(true); - WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT, null, null, null); - assertEquals(100, insets.getStableInsetBottom()); - assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all())); - assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets()); - assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all())); - assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars())); - assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime())); + public void testCalculateInsets_imeAndNav() throws Exception{ + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, null); + assertEquals(100, insets.getStableInsetBottom()); + assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.systemBars())); + assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets()); + assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all())); + assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars())); + assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime())); + } } @Test - public void testCalculateInsets_navRightStatusTop() { + public void testCalculateInsets_navRightStatusTop() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); + mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT, null, null, 0, null); + assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); + assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar())); + assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars())); + } + } + + @Test + public void testCalculateInsets_imeIgnoredWithoutAdjustResize() { mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); mState.getSource(TYPE_TOP_BAR).setVisible(true); - mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); - mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT, null, null, null); - assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); - assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar())); - assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars())); + DisplayCutout.NO_CUTOUT, null, null, 0, null); + assertEquals(0, insets.getSystemWindowInsetBottom()); + assertTrue(insets.isVisible(ime())); } @Test @@ -106,7 +130,7 @@ public class InsetsStateTest { mState.getSource(TYPE_IME).setVisible(true); mState.removeSource(TYPE_IME); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT, null, null, null); + DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, null); assertEquals(0, insets.getSystemWindowInsetBottom()); } @@ -114,14 +138,14 @@ public class InsetsStateTest { public void testEquals_differentRect() { mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10)); - assertNotEquals(mState, mState2); + assertNotEqualsAndHashCode(); } @Test public void testEquals_differentSource() { mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); - assertNotEquals(mState, mState2); + assertNotEqualsAndHashCode(); } @Test @@ -130,7 +154,7 @@ public class InsetsStateTest { mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); - assertEquals(mState, mState2); + assertEqualsAndHashCode(); } @Test @@ -138,7 +162,21 @@ public class InsetsStateTest { mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); mState.getSource(TYPE_IME).setVisible(true); mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); - assertNotEquals(mState, mState2); + assertNotEqualsAndHashCode(); + } + + @Test + public void testEquals_differentFrame() { + mState.setDisplayFrame(new Rect(0, 1, 2, 3)); + mState.setDisplayFrame(new Rect(4, 5, 6, 7)); + assertNotEqualsAndHashCode(); + } + + @Test + public void testEquals_sameFrame() { + mState.setDisplayFrame(new Rect(0, 1, 2, 3)); + mState2.setDisplayFrame(new Rect(0, 1, 2, 3)); + assertEqualsAndHashCode(); } @Test @@ -148,6 +186,7 @@ public class InsetsStateTest { mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); Parcel p = Parcel.obtain(); mState.writeToParcel(p, 0 /* flags */); + p.setDataPosition(0); mState2.readFromParcel(p); p.recycle(); assertEquals(mState, mState2); @@ -161,4 +200,14 @@ public class InsetsStateTest { assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_3)); assertFalse(InsetsState.getDefaultVisibility(TYPE_IME)); } + + private void assertEqualsAndHashCode() { + assertEquals(mState, mState2); + assertEquals(mState.hashCode(), mState2.hashCode()); + } + + private void assertNotEqualsAndHashCode() { + assertNotEquals(mState, mState2); + assertNotEquals(mState.hashCode(), mState2.hashCode()); + } } diff --git a/core/tests/coretests/src/android/view/accessibility/FindViewByIdTest.java b/core/tests/coretests/src/android/view/accessibility/FindViewByIdTest.java new file mode 100644 index 000000000000..da6ecb45bd5c --- /dev/null +++ b/core/tests/coretests/src/android/view/accessibility/FindViewByIdTest.java @@ -0,0 +1,100 @@ +/* + * 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.view.accessibility; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.app.Activity; +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.test.InstrumentationRegistry; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; + +@MediumTest +public class FindViewByIdTest { + + @Rule + public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class); + + private Context getContext() { + return InstrumentationRegistry.getTargetContext(); + } + + private Activity getActivity() { + return mActivityRule.getActivity(); + } + + @UiThreadTest + @Test + public void testFindViewById() { + LinearLayout contentView = new LinearLayout(getContext()); + getActivity().setContentView(contentView); + View child1 = new View(getContext()); + View child2 = new View(getContext()); + child1.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + child2.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + + contentView.addView(child1); + contentView.addView(child2); + View result = AccessibilityNodeIdManager.getInstance().findView( + child2.getAccessibilityViewId()); + assertEquals(result, child2); + } + + @UiThreadTest + @Test + public void testFindViewByIdReturnNullIfRemovedFromHierarchy() { + LinearLayout contentView = new LinearLayout(getContext()); + getActivity().setContentView(contentView); + View child1 = new View(getContext()); + View child2 = new View(getContext()); + contentView.addView(child1); + contentView.addView(child2); + child1.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + child2.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + + contentView.removeView(child1); + View result = AccessibilityNodeIdManager.getInstance().findView( + child1.getAccessibilityViewId()); + assertNull(result); + } + + @UiThreadTest + @Test + public void testFindViewByIdReturnNullIfNotImportant() { + LinearLayout contentView = new LinearLayout(getContext()); + getActivity().setContentView(contentView); + View child1 = new View(getContext()); + View child2 = new View(getContext()); + child2.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + + contentView.addView(child1); + contentView.addView(child2); + + View result = AccessibilityNodeIdManager.getInstance().findView( + child1.getAccessibilityViewId()); + assertNull(result); + } +} diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java new file mode 100644 index 000000000000..f325d8943ea3 --- /dev/null +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java @@ -0,0 +1,229 @@ +/* + * 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.view.contentcapture; + +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.os.Parcel; +import android.os.SystemClock; +import android.view.autofill.AutofillId; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.ArrayList; + +/** + * Unit test for {@link ContentCaptureEvent}. + * + * <p>To run it: + * {@code atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureEventTest} + */ +@RunWith(JUnit4.class) +public class ContentCaptureEventTest { + + private static final long MY_EPOCH = SystemClock.uptimeMillis(); + + // Not using @Mock because it's final - no need to be fancy here.... + private final ContentCaptureContext mClientContext = new ContentCaptureContext.Builder() + .setAction("WHATEVER").build(); + + @Test + public void testSetAutofillId_null() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + assertThrows(NullPointerException.class, () -> event.setAutofillId(null)); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + } + + @Test + public void testSetAutofillIds_null() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + assertThrows(NullPointerException.class, () -> event.setAutofillIds(null)); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + } + + @Test + public void testAddAutofillId_null() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + assertThrows(NullPointerException.class, () -> event.addAutofillId(null)); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + } + + @Test + public void testSetAutofillId() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + final AutofillId id = new AutofillId(108); + event.setAutofillId(id); + assertThat(event.getId()).isEqualTo(id); + assertThat(event.getIds()).isNull(); + } + + @Test + public void testSetAutofillIds() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + final AutofillId id = new AutofillId(108); + final ArrayList<AutofillId> ids = new ArrayList<>(1); + ids.add(id); + event.setAutofillIds(ids); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).containsExactly(id); + } + + @Test + public void testAddAutofillId() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + final AutofillId id1 = new AutofillId(108); + event.addAutofillId(id1); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).containsExactly(id1); + + final AutofillId id2 = new AutofillId(666); + event.addAutofillId(id2); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).containsExactly(id1, id2).inOrder(); + } + + @Test + public void testAddAutofillId_afterSetId() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + final AutofillId id1 = new AutofillId(108); + event.setAutofillId(id1); + assertThat(event.getId()).isEqualTo(id1); + assertThat(event.getIds()).isNull(); + + final AutofillId id2 = new AutofillId(666); + event.addAutofillId(id2); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).containsExactly(id1, id2).inOrder(); + } + + @Test + public void testAddAutofillId_afterSetIds() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED); + + final AutofillId id1 = new AutofillId(108); + final ArrayList<AutofillId> ids = new ArrayList<>(1); + ids.add(id1); + event.setAutofillIds(ids); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).containsExactly(id1); + + final AutofillId id2 = new AutofillId(666); + event.addAutofillId(id2); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).containsExactly(id1, id2).inOrder(); + } + + @Test + public void testSessionStarted_directly() { + final ContentCaptureEvent event = newEventForSessionStarted(); + assertSessionStartedEvent(event); + } + + @Test + public void testSessionStarted_throughParcel() { + final ContentCaptureEvent event = newEventForSessionStarted(); + final ContentCaptureEvent clone = cloneThroughParcel(event); + assertSessionStartedEvent(clone); + } + + private ContentCaptureEvent newEventForSessionStarted() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_SESSION_STARTED) + .setClientContext(mClientContext) + .setParentSessionId("108"); + assertThat(event).isNotNull(); + return event; + } + + private void assertSessionStartedEvent(ContentCaptureEvent event) { + assertThat(event.getType()).isEqualTo(TYPE_SESSION_STARTED); + assertThat(event.getEventTime()).isAtLeast(MY_EPOCH); + assertThat(event.getSessionId()).isEqualTo("42"); + assertThat(event.getParentSessionId()).isEqualTo("108"); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + assertThat(event.getText()).isNull(); + assertThat(event.getViewNode()).isNull(); + final ContentCaptureContext clientContext = event.getClientContext(); + assertThat(clientContext.getAction()).isEqualTo("WHATEVER"); + } + + @Test + public void testSessionFinished_directly() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_SESSION_FINISHED) + .setParentSessionId("108"); + assertThat(event).isNotNull(); + assertSessionFinishedEvent(event); + } + + @Test + public void testSessionFinished_throughParcel() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_SESSION_FINISHED) + .setClientContext(mClientContext) // should not be writting to parcel + .setParentSessionId("108"); + assertThat(event).isNotNull(); + final ContentCaptureEvent clone = cloneThroughParcel(event); + assertSessionFinishedEvent(clone); + } + + private void assertSessionFinishedEvent(ContentCaptureEvent event) { + assertThat(event.getType()).isEqualTo(TYPE_SESSION_FINISHED); + assertThat(event.getEventTime()).isAtLeast(MY_EPOCH); + assertThat(event.getSessionId()).isEqualTo("42"); + assertThat(event.getParentSessionId()).isEqualTo("108"); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + assertThat(event.getText()).isNull(); + assertThat(event.getViewNode()).isNull(); + assertThat(event.getClientContext()).isNull(); + } + + private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) { + Parcel parcel = Parcel.obtain(); + + try { + // Write to parcel + parcel.setDataPosition(0); // Sanity / paranoid check + event.writeToParcel(parcel, 0); + + // Read from parcel + parcel.setDataPosition(0); + ContentCaptureEvent clone = ContentCaptureEvent.CREATOR.createFromParcel(parcel); + assertThat(clone).isNotNull(); + return clone; + } finally { + parcel.recycle(); + } + } + +} diff --git a/core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java b/core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java deleted file mode 100644 index bebb2a89f480..000000000000 --- a/core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java +++ /dev/null @@ -1,88 +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 android.view.contentcapture; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.net.Uri; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -/** - * Unit test for {@link UserDataRemovalRequest}. - * - * <p>To run it: - * {@code atest FrameworksCoreTests:android.view.contentcapture.UserDataRemovalRequestTest} - */ -@RunWith(MockitoJUnitRunner.class) -public class UserDataRemovalRequestTest { - - @Mock - private final Uri mUri = Uri.parse("content://com.example/"); - - private UserDataRemovalRequest.Builder mBuilder = new UserDataRemovalRequest.Builder(); - - @Test - public void testBuilder_addUri_invalid() { - assertThrows(NullPointerException.class, () -> mBuilder.addUri(null, false)); - } - - @Test - public void testBuilder_addUri_valid() { - assertThat(mBuilder.addUri(mUri, false)).isNotNull(); - assertThat(mBuilder.addUri(Uri.parse("content://com.example2"), true)).isNotNull(); - } - - @Test - public void testBuilder_addUriAfterForEverything() { - assertThat(mBuilder.forEverything()).isNotNull(); - assertThrows(IllegalStateException.class, () -> mBuilder.addUri(mUri, false)); - } - - @Test - public void testBuilder_forEverythingAfterAddingUri() { - assertThat(mBuilder.addUri(mUri, false)).isNotNull(); - assertThrows(IllegalStateException.class, () -> mBuilder.forEverything()); - } - - @Test - public void testBuild_invalid() { - assertThrows(IllegalStateException.class, () -> mBuilder.build()); - } - - @Test - public void testBuild_valid() { - assertThat(new UserDataRemovalRequest.Builder().forEverything().build()) - .isNotNull(); - assertThat(new UserDataRemovalRequest.Builder().addUri(mUri, false).build()) - .isNotNull(); - } - - @Test - public void testNoMoreInteractionsAfterBuild() { - assertThat(mBuilder.forEverything().build()).isNotNull(); - - assertThrows(IllegalStateException.class, () -> mBuilder.addUri(mUri, false)); - assertThrows(IllegalStateException.class, () -> mBuilder.forEverything()); - assertThrows(IllegalStateException.class, () -> mBuilder.build()); - - } -} diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java index b84a098574c2..213cd405e903 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java @@ -18,29 +18,18 @@ package android.view.contentcapture; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.assertThrows; - import android.content.Context; import android.graphics.Matrix; -import android.os.Bundle; -import android.os.LocaleList; -import android.os.Parcel; +import android.support.test.InstrumentationRegistry; import android.view.View; import android.view.ViewStructure.HtmlInfo; -import android.view.autofill.AutofillId; -import android.view.autofill.AutofillValue; import android.view.contentcapture.ViewNode.ViewStructureImpl; -import android.widget.FrameLayout; - -import androidx.test.InstrumentationRegistry; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.util.Locale; - /** * Unit tests for {@link ViewNode}. * @@ -55,100 +44,6 @@ public class ViewNodeTest { private HtmlInfo mHtmlInfoMock; @Test - public void testAutofillIdMethods_orphanView() { - View view = new View(mContext); - AutofillId initialId = new AutofillId(42); - view.setAutofillId(initialId); - - ViewStructureImpl structure = new ViewStructureImpl(view); - ViewNode node = structure.getNode(); - - assertThat(node.getAutofillId()).isEqualTo(initialId); - assertThat(node.getParentAutofillId()).isNull(); - - AutofillId newId = new AutofillId(108); - structure.setAutofillId(newId); - assertThat(node.getAutofillId()).isEqualTo(newId); - assertThat(node.getParentAutofillId()).isNull(); - - structure.setAutofillId(new AutofillId(66), 6); - assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6)); - assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66)); - } - - @Test - public void testAutofillIdMethods_parentedView() { - FrameLayout parent = new FrameLayout(mContext); - AutofillId initialParentId = new AutofillId(48); - parent.setAutofillId(initialParentId); - - View child = new View(mContext); - AutofillId initialChildId = new AutofillId(42); - child.setAutofillId(initialChildId); - - parent.addView(child); - - ViewStructureImpl structure = new ViewStructureImpl(child); - ViewNode node = structure.getNode(); - - assertThat(node.getAutofillId()).isEqualTo(initialChildId); - assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); - - AutofillId newChildId = new AutofillId(108); - structure.setAutofillId(newChildId); - assertThat(node.getAutofillId()).isEqualTo(newChildId); - assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); - - AutofillId newParentId = new AutofillId(15162342); - parent.setAutofillId(newParentId); - assertThat(node.getAutofillId()).isEqualTo(newChildId); - assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); - - structure.setAutofillId(new AutofillId(66), 6); - assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6)); - assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66)); - } - - @Test - public void testAutofillIdMethods_explicitIdsConstructor() { - AutofillId initialParentId = new AutofillId(42); - ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666); - ViewNode node = structure.getNode(); - - assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666)); - assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); - - AutofillId newChildId = new AutofillId(108); - structure.setAutofillId(newChildId); - assertThat(node.getAutofillId()).isEqualTo(newChildId); - assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); - - structure.setAutofillId(new AutofillId(66), 6); - assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6)); - assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66)); - } - - @Test - public void testInvalidSetters() { - View view = new View(mContext); - AutofillId initialId = new AutofillId(42); - view.setAutofillId(initialId); - - ViewStructureImpl structure = new ViewStructureImpl(view); - ViewNode node = structure.getNode(); - assertThat(node.getAutofillId()).isEqualTo(initialId); // sanity check - - assertThrows(NullPointerException.class, () -> structure.setAutofillId(null)); - assertThat(node.getAutofillId()).isEqualTo(initialId); // invariant - - assertThrows(NullPointerException.class, () -> structure.setAutofillId(null, 666)); - assertThat(node.getAutofillId()).isEqualTo(initialId); // invariant - - assertThrows(NullPointerException.class, () -> structure.setTextIdEntry(null)); - assertThat(node.getTextIdEntry()).isNull(); - } - - @Test public void testUnsupportedProperties() { View view = new View(mContext); @@ -190,273 +85,4 @@ public class ViewNodeTest { structure.setTransformation(Matrix.IDENTITY_MATRIX); assertThat(node.getTransformation()).isNull(); } - - @Test - public void testValidProperties_directly() { - ViewStructureImpl structure = newSimpleStructure(); - assertSimpleStructure(structure); - assertSimpleNode(structure.getNode()); - } - - @Test - public void testValidProperties_throughParcel() { - ViewStructureImpl structure = newSimpleStructure(); - final ViewNode node = structure.getNode(); - assertSimpleNode(node); // sanity check - - final ViewNode clone = cloneThroughParcel(node); - assertSimpleNode(clone); - } - - @Test - public void testComplexText_directly() { - ViewStructureImpl structure = newStructureWithComplexText(); - assertStructureWithComplexText(structure); - assertNodeWithComplexText(structure.getNode()); - } - - @Test - public void testComplexText_throughParcel() { - ViewStructureImpl structure = newStructureWithComplexText(); - final ViewNode node = structure.getNode(); - assertNodeWithComplexText(node); // sanity check - - ViewNode clone = cloneThroughParcel(node); - assertNodeWithComplexText(clone); - } - - @Test - public void testVisibility() { - // Visibility is a special case becase it use flag masks, so we want to make sure it works - // fine - View view = new View(mContext); - ViewStructureImpl structure = new ViewStructureImpl(view); - ViewNode node = structure.getNode(); - - structure.setVisibility(View.VISIBLE); - assertThat(node.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE); - - structure.setVisibility(View.GONE); - assertThat(node.getVisibility()).isEqualTo(View.GONE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.GONE); - - structure.setVisibility(View.VISIBLE); - assertThat(node.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE); - - structure.setVisibility(View.INVISIBLE); - assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.INVISIBLE); - - structure.setVisibility(View.INVISIBLE | View.GONE); - assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE | View.GONE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.INVISIBLE | View.GONE); - - - final int invalidValue = Math.max(Math.max(View.VISIBLE, View.INVISIBLE), View.GONE) * 2; - structure.setVisibility(View.VISIBLE); - structure.setVisibility(invalidValue); // should be ignored - assertThat(node.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE); - - structure.setVisibility(View.GONE | invalidValue); - assertThat(node.getVisibility()).isEqualTo(View.GONE); - assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.GONE); - } - - /** - * Creates a {@link ViewStructureImpl} that can be asserted through - * {@link #assertSimpleNode(ViewNode)}. - */ - private ViewStructureImpl newSimpleStructure() { - View view = new View(mContext); - view.setAutofillId(new AutofillId(42)); - - ViewStructureImpl structure = new ViewStructureImpl(view); - - // Basic properties - structure.setText("Text is set!"); - structure.setClassName("Classy!"); - structure.setContentDescription("Described I am!"); - structure.setVisibility(View.INVISIBLE); - - // Autofill properties - structure.setAutofillType(View.AUTOFILL_TYPE_TEXT); - structure.setAutofillHints(new String[] { "Auto", "Man" }); - structure.setAutofillOptions(new String[] { "Maybe" }); - structure.setAutofillValue(AutofillValue.forText("Malkovich")); - - // Extra text properties - structure.setMinTextEms(6); - structure.setMaxTextLength(66); - structure.setMaxTextEms(666); - structure.setInputType(42); - structure.setTextIdEntry("TEXT, Y U NO ENTRY?"); - structure.setLocaleList(new LocaleList(Locale.US, Locale.ENGLISH)); - - // Resource id - structure.setId(16, "package.name", "type.name", "entry.name"); - - // Dimensions - structure.setDimens(4, 8, 15, 16, 23, 42); - - // Boolean properties - structure.setAssistBlocked(true); - structure.setEnabled(true); - structure.setClickable(true); - structure.setLongClickable(true); - structure.setContextClickable(true); - structure.setFocusable(true); - structure.setFocused(true); - structure.setAccessibilityFocused(true); - structure.setChecked(true); - structure.setActivated(true); - structure.setOpaque(true); - - // Bundle - assertThat(structure.hasExtras()).isFalse(); - final Bundle bundle = structure.getExtras(); - assertThat(bundle).isNotNull(); - bundle.putString("Marlon", "Bundle"); - assertThat(structure.hasExtras()).isTrue(); - return structure; - } - - /** - * Asserts the properties of a {@link ViewNode} that was created by - * {@link #newSimpleStructure()}. - */ - private void assertSimpleNode(ViewNode node) { - - // Basic properties - assertThat(node.getAutofillId()).isEqualTo(new AutofillId(42)); - assertThat(node.getParentAutofillId()).isNull(); - assertThat(node.getText()).isEqualTo("Text is set!"); - assertThat(node.getClassName()).isEqualTo("Classy!"); - assertThat(node.getContentDescription().toString()).isEqualTo("Described I am!"); - assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE); - - // Autofill properties - assertThat(node.getAutofillType()).isEqualTo(View.AUTOFILL_TYPE_TEXT); - assertThat(node.getAutofillHints()).asList().containsExactly("Auto", "Man").inOrder(); - assertThat(node.getAutofillOptions()).asList().containsExactly("Maybe").inOrder(); - assertThat(node.getAutofillValue().getTextValue()).isEqualTo("Malkovich"); - - // Extra text properties - assertThat(node.getMinTextEms()).isEqualTo(6); - assertThat(node.getMaxTextLength()).isEqualTo(66); - assertThat(node.getMaxTextEms()).isEqualTo(666); - assertThat(node.getInputType()).isEqualTo(42); - assertThat(node.getTextIdEntry()).isEqualTo("TEXT, Y U NO ENTRY?"); - assertThat(node.getLocaleList()).isEqualTo(new LocaleList(Locale.US, Locale.ENGLISH)); - - // Resource id - assertThat(node.getId()).isEqualTo(16); - assertThat(node.getIdPackage()).isEqualTo("package.name"); - assertThat(node.getIdType()).isEqualTo("type.name"); - assertThat(node.getIdEntry()).isEqualTo("entry.name"); - - // Dimensions - assertThat(node.getLeft()).isEqualTo(4); - assertThat(node.getTop()).isEqualTo(8); - assertThat(node.getScrollX()).isEqualTo(15); - assertThat(node.getScrollY()).isEqualTo(16); - assertThat(node.getWidth()).isEqualTo(23); - assertThat(node.getHeight()).isEqualTo(42); - - // Boolean properties - assertThat(node.isAssistBlocked()).isTrue(); - assertThat(node.isEnabled()).isTrue(); - assertThat(node.isClickable()).isTrue(); - assertThat(node.isLongClickable()).isTrue(); - assertThat(node.isContextClickable()).isTrue(); - assertThat(node.isFocusable()).isTrue(); - assertThat(node.isFocused()).isTrue(); - assertThat(node.isAccessibilityFocused()).isTrue(); - assertThat(node.isChecked()).isTrue(); - assertThat(node.isActivated()).isTrue(); - assertThat(node.isOpaque()).isTrue(); - - // Bundle - final Bundle bundle = node.getExtras(); - assertThat(bundle).isNotNull(); - assertThat(bundle.size()).isEqualTo(1); - assertThat(bundle.getString("Marlon")).isEqualTo("Bundle"); - } - - /** - * Asserts the properties of a {@link ViewStructureImpl} that was created by - * {@link #newSimpleStructure()}. - */ - private void assertSimpleStructure(ViewStructureImpl structure) { - assertThat(structure.getAutofillId()).isEqualTo(new AutofillId(42)); - assertThat(structure.getText()).isEqualTo("Text is set!"); - - // Bundle - final Bundle bundle = structure.getExtras(); - assertThat(bundle.size()).isEqualTo(1); - assertThat(bundle.getString("Marlon")).isEqualTo("Bundle"); - } - - /** - * Creates a {@link ViewStructureImpl} with "complex" text properties (such as selection); it - * can be asserted through {@link #assertNodeWithComplexText(ViewNode)}. - */ - private ViewStructureImpl newStructureWithComplexText() { - View view = new View(mContext); - ViewStructureImpl structure = new ViewStructureImpl(view); - structure.setText("IGNORE ME!"); - structure.setText("Now we're talking!", 4, 8); - structure.setHint("Soylent Green is SPOILER ALERT"); - structure.setTextStyle(15.0f, 16, 23, 42); - structure.setTextLines(new int[] {4, 8, 15} , new int[] {16, 23, 42}); - return structure; - } - - /** - * Asserts the properties of a {@link ViewNode} that was created by - * {@link #newStructureWithComplexText()}. - */ - private void assertNodeWithComplexText(ViewNode node) { - assertThat(node.getText()).isEqualTo("Now we're talking!"); - assertThat(node.getTextSelectionStart()).isEqualTo(4); - assertThat(node.getTextSelectionEnd()).isEqualTo(8); - assertThat(node.getHint()).isEqualTo("Soylent Green is SPOILER ALERT"); - assertThat(node.getTextSize()).isWithin(1.0e-10f).of(15.0f); - assertThat(node.getTextColor()).isEqualTo(16); - assertThat(node.getTextBackgroundColor()).isEqualTo(23); - assertThat(node.getTextStyle()).isEqualTo(42); - assertThat(node.getTextLineCharOffsets()).asList().containsExactly(4, 8, 15).inOrder(); - assertThat(node.getTextLineBaselines()).asList().containsExactly(16, 23, 42).inOrder(); - } - - /** - * Asserts the properties of a {@link ViewStructureImpl} that was created by - * {@link #newStructureWithComplexText()}. - */ - private void assertStructureWithComplexText(ViewStructureImpl structure) { - assertThat(structure.getText()).isEqualTo("Now we're talking!"); - assertThat(structure.getTextSelectionStart()).isEqualTo(4); - assertThat(structure.getTextSelectionEnd()).isEqualTo(8); - assertThat(structure.getHint()).isEqualTo("Soylent Green is SPOILER ALERT"); - } - - private ViewNode cloneThroughParcel(ViewNode node) { - Parcel parcel = Parcel.obtain(); - - try { - // Write to parcel - parcel.setDataPosition(0); // Sanity / paranoid check - ViewNode.writeToParcel(parcel, node, 0); - - // Read from parcel - parcel.setDataPosition(0); - ViewNode clone = ViewNode.readFromParcel(parcel); - assertThat(clone).isNotNull(); - return clone; - } finally { - parcel.recycle(); - } - } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java index 08ad62ab843d..d9dac314fc7c 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java @@ -41,6 +41,7 @@ public class TemplateClassificationIntentFactoryTest { private static final String TEXT = "text"; private static final String TITLE = "Map"; + private static final String DESCRIPTION = "Opens in Maps"; private static final String ACTION = Intent.ACTION_VIEW; @Mock @@ -57,19 +58,6 @@ public class TemplateClassificationIntentFactoryTest { @Test public void create_foreignText() { - RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, - null, - ACTION, - null, - null, - null, - null, - null, - null, - null - ); - AnnotatorModel.ClassificationResult classificationResult = new AnnotatorModel.ClassificationResult( TextClassifier.TYPE_ADDRESS, @@ -81,7 +69,7 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, - new RemoteActionTemplate[]{remoteActionTemplate}); + createRemoteActionTemplates()); List<TextClassifierImpl.LabeledIntent> intents = mTemplateClassificationIntentFactory.create( @@ -106,19 +94,6 @@ public class TemplateClassificationIntentFactoryTest { @Test public void create_notForeignText() { - RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, - null, - ACTION, - null, - null, - null, - null, - null, - null, - null - ); - AnnotatorModel.ClassificationResult classificationResult = new AnnotatorModel.ClassificationResult( TextClassifier.TYPE_ADDRESS, @@ -130,7 +105,7 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, - new RemoteActionTemplate[]{remoteActionTemplate}); + createRemoteActionTemplates()); List<TextClassifierImpl.LabeledIntent> intents = mTemplateClassificationIntentFactory.create( @@ -147,4 +122,21 @@ public class TemplateClassificationIntentFactoryTest { assertThat(intent.getAction()).isEqualTo(ACTION); assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue(); } + + private static RemoteActionTemplate[] createRemoteActionTemplates() { + return new RemoteActionTemplate[]{ + new RemoteActionTemplate( + TITLE, + DESCRIPTION, + ACTION, + null, + null, + null, + null, + null, + null, + null + ) + }; + } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java index 0d364a329de4..a1158a7cbb6c 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java @@ -81,7 +81,6 @@ public class TemplateIntentFactoryTest { REQUEST_CODE ); - List<TextClassifierImpl.LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate}); @@ -97,23 +96,22 @@ public class TemplateIntentFactoryTest { assertThat(intent.getFlags()).isEqualTo(FLAG); assertThat(intent.getCategories()).containsExactly((Object[]) CATEGORY); assertThat(intent.getPackage()).isNull(); - assertThat( - intent.getStringExtra(KEY_ONE)).isEqualTo(VALUE_ONE); + assertThat(intent.getStringExtra(KEY_ONE)).isEqualTo(VALUE_ONE); assertThat(intent.getIntExtra(KEY_TWO, 0)).isEqualTo(VALUE_TWO); assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue(); } @Test - public void create_packageIsNotNull() { + public void normalizesScheme() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( TITLE, DESCRIPTION, ACTION, - DATA, + "HTTp://www.android.com", TYPE, FLAG, CATEGORY, - PACKAGE_NAME, + /* packageName */ null, NAMED_VARIANTS, REQUEST_CODE ); @@ -121,15 +119,16 @@ public class TemplateIntentFactoryTest { List<TextClassifierImpl.LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); - assertThat(intents).hasSize(0); + String data = intents.get(0).getIntent().getData().toString(); + assertThat(data).isEqualTo("http://www.android.com"); } @Test public void create_minimal() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - null, - null, - null, + TITLE, + DESCRIPTION, + ACTION, null, null, null, @@ -142,15 +141,14 @@ public class TemplateIntentFactoryTest { List<TextClassifierImpl.LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate}); - assertThat(intents).hasSize(1); TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0); - assertThat(labeledIntent.getTitle()).isNull(); - assertThat(labeledIntent.getDescription()).isNull(); + assertThat(labeledIntent.getTitle()).isEqualTo(TITLE); + assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION); assertThat(labeledIntent.getRequestCode()).isEqualTo( TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE); Intent intent = labeledIntent.getIntent(); - assertThat(intent.getAction()).isNull(); + assertThat(intent.getAction()).isEqualTo(ACTION); assertThat(intent.getData()).isNull(); assertThat(intent.getType()).isNull(); assertThat(intent.getFlags()).isEqualTo(0); @@ -158,4 +156,98 @@ public class TemplateIntentFactoryTest { assertThat(intent.getPackage()).isNull(); assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue(); } + + @Test + public void invalidTemplate_nullTemplate() { + RemoteActionTemplate remoteActionTemplate = null; + + List<TextClassifierImpl.LabeledIntent> intents = + mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); + + assertThat(intents).isEmpty(); + } + + @Test + public void invalidTemplate_nonEmptyPackageName() { + RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( + TITLE, + DESCRIPTION, + ACTION, + DATA, + TYPE, + FLAG, + CATEGORY, + PACKAGE_NAME, + NAMED_VARIANTS, + REQUEST_CODE + ); + + List<TextClassifierImpl.LabeledIntent> intents = + mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); + + assertThat(intents).isEmpty(); + } + + @Test + public void invalidTemplate_emptyTitle() { + RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( + null, + DESCRIPTION, + ACTION, + null, + null, + null, + null, + null, + null, + null + ); + + List<TextClassifierImpl.LabeledIntent> intents = + mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); + + assertThat(intents).isEmpty(); + } + + @Test + public void invalidTemplate_emptyDescription() { + RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( + TITLE, + null, + ACTION, + null, + null, + null, + null, + null, + null, + null + ); + + List<TextClassifierImpl.LabeledIntent> intents = + mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); + + assertThat(intents).isEmpty(); + } + + @Test + public void invalidTemplate_emptyIntentAction() { + RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( + TITLE, + DESCRIPTION, + null, + null, + null, + null, + null, + null, + null, + null + ); + + List<TextClassifierImpl.LabeledIntent> intents = + mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); + + assertThat(intents).isEmpty(); + } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java index 32bafec4081a..fe2a6608c781 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java @@ -195,11 +195,13 @@ public class TextClassificationConstantsTest { assertWithMessage("in_app_conversation_action_types_default") .that(constants.getInAppConversationActionTypes()) .containsExactly("text_reply", "create_reminder", "call_phone", "open_url", - "send_email", "send_sms", "track_flight", "view_calendar", "view_map"); + "send_email", "send_sms", "track_flight", "view_calendar", "view_map", + "add_contact"); assertWithMessage("notification_conversation_action_types_default") .that(constants.getNotificationConversationActionTypes()) .containsExactly("text_reply", "create_reminder", "call_phone", "open_url", - "send_email", "send_sms", "track_flight", "view_calendar", "view_map"); + "send_email", "send_sms", "track_flight", "view_calendar", "view_map", + "add_contact"); assertWithMessage("lang_id_threshold_override") .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f); } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index 582be9d51731..bdd03707c1f1 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -176,6 +176,7 @@ public class TextClassifierTest { TextClassification classification = mClassifier.classifyText(request); assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL)); + assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW)); } @Test @@ -207,6 +208,7 @@ public class TextClassifierTest { TextClassification classification = mClassifier.classifyText(request); assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL)); + assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW)); } @Test @@ -517,6 +519,24 @@ public class TextClassifierTest { }; } + private static Matcher<TextClassification> containsIntentWithAction(final String action) { + return new BaseMatcher<TextClassification>() { + @Override + public boolean matches(Object o) { + if (o instanceof TextClassification) { + TextClassification result = (TextClassification) o; + return ExtrasUtils.findAction(result, action) != null; + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("intent action=").appendValue(action); + } + }; + } + private static Matcher<TextLanguage> isTextLanguage(final String languageTag) { return new BaseMatcher<TextLanguage>() { @Override diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java index 73af56743b5f..1980a604fdd2 100644 --- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java @@ -91,8 +91,8 @@ public class TextClassifierEventTronLoggerTest { .isEqualTo(ConversationAction.TYPE_CALL_PHONE); assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE)) .isWithin(0.00001f).of(0.5f); - assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME)) - .isEqualTo(EVENT_TIME); + // Never write event time. + assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME)).isNull(); assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME); assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE)) .isEqualTo(WIDGET_TYPE); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index b6f56ada445e..3d59835a6719 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -27,7 +27,9 @@ import static com.android.internal.app.ChooserWrapperActivity.sOverrides; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,6 +45,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.metrics.LogMaker; import android.net.Uri; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -51,11 +54,14 @@ import androidx.test.rule.ActivityTestRule; import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.util.ArrayList; @@ -66,6 +72,11 @@ import java.util.List; */ @RunWith(AndroidJUnit4.class) public class ChooserActivityTest { + + private static final int CONTENT_PREVIEW_IMAGE = 1; + private static final int CONTENT_PREVIEW_FILE = 2; + private static final int CONTENT_PREVIEW_TEXT = 3; + @Rule public ActivityTestRule<ChooserWrapperActivity> mActivityRule = new ActivityTestRule<>(ChooserWrapperActivity.class, false, @@ -402,16 +413,15 @@ public class ChooserActivityTest { createResolvedComponentsForTestWithOtherProfile(1); when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( - Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ChooserWrapperActivity activity = mActivityRule .launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); onView(withId(R.id.copy_button)).perform(click()); - ClipboardManager clipboard = (ClipboardManager) activity.getSystemService( Context.CLIPBOARD_SERVICE); ClipData clipData = clipboard.getPrimaryClip(); @@ -488,8 +498,8 @@ public class ChooserActivityTest { List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed())); @@ -498,6 +508,93 @@ public class ChooserActivityTest { onView(withId(R.id.content_preview_image_3_small)).check(matches(isDisplayed())); } + @Test + public void testOnCreateLogging() { + Intent sendIntent = createSendTextIntent(); + sendIntent.setType("TestType"); + + MetricsLogger mockLogger = sOverrides.metricsLogger; + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test")); + waitForIdle(); + verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture()); + assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getTaggedData(MetricsProto.MetricsEvent.FIELD_TIME_TO_APP_TARGETS), + is(notNullValue())); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getTaggedData(MetricsProto.MetricsEvent.FIELD_SHARESHEET_MIMETYPE), + is("TestType")); + } + + @Test + public void testEmptyPreviewLogging() { + Intent sendIntent = createSendTextIntentWithPreview(null, null); + + MetricsLogger mockLogger = sOverrides.metricsLogger; + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test")); + waitForIdle(); + verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture()); + // First invocation is from onCreate + assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), + is(CONTENT_PREVIEW_TEXT)); + } + + @Test + public void testTitlePreviewLogging() { + Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null); + + MetricsLogger mockLogger = sOverrides.metricsLogger; + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture()); + // First invocation is from onCreate + assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), + is(CONTENT_PREVIEW_TEXT)); + } + + @Test + public void testImagePreviewLogging() { + Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/" + + com.android.frameworks.coretests.R.drawable.test320x240); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + + Intent sendIntent = createSendImageIntentWithPreview(uris); + sOverrides.previewThumbnail = createBitmap(); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + MetricsLogger mockLogger = sOverrides.metricsLogger; + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); + // First invocation is from onCreate + assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), + is(CONTENT_PREVIEW_IMAGE)); + assertThat(logMakerCaptor.getAllValues().get(2).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + assertThat(logMakerCaptor.getAllValues().get(2).getSubtype(), + is(CONTENT_PREVIEW_IMAGE)); + } + private Intent createSendTextIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index ec8122fb2e47..f60467bd3df2 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -25,6 +25,8 @@ import android.graphics.Bitmap; import android.net.Uri; import android.util.Size; +import com.android.internal.logging.MetricsLogger; + import java.util.function.Function; public class ChooserWrapperActivity extends ChooserActivity { @@ -94,6 +96,11 @@ public class ChooserWrapperActivity extends ChooserActivity { return super.isImageType(mimeType); } + @Override + protected MetricsLogger getMetricsLogger() { + return sOverrides.metricsLogger; + } + /** * We cannot directly mock the activity created since instrumentation creates it. * <p> @@ -106,6 +113,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public ResolverListController resolverListController; public Boolean isVoiceInteraction; public Bitmap previewThumbnail; + public MetricsLogger metricsLogger; public void reset() { onSafelyStartCallback = null; @@ -113,6 +121,7 @@ public class ChooserWrapperActivity extends ChooserActivity { createPackageManager = null; previewThumbnail = null; resolverListController = mock(ResolverListController.class); + metricsLogger = mock(MetricsLogger.class); } } } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 0f35918232b4..6770ae19ce3d 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -104,16 +104,6 @@ applications that come with the platform <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> - <privapp-permissions package="com.android.omadm.service"> - <permission name="android.permission.CHANGE_CONFIGURATION"/> - <permission name="android.permission.CONNECTIVITY_INTERNAL"/> - <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> - <permission name="android.permission.MODIFY_PHONE_STATE"/> - <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> - <permission name="android.permission.WRITE_APN_SETTINGS"/> - <permission name="android.permission.WRITE_SECURE_SETTINGS"/> - </privapp-permissions> - <privapp-permissions package="com.android.packageinstaller"> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/> diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 229923ca8202..5d8ba938f6d5 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -36,7 +36,10 @@ import java.nio.channels.FileChannel; * A family of typefaces with different styles. * * @hide + * + * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. */ +@Deprecated public class FontFamily { private static String TAG = "FontFamily"; @@ -51,20 +54,28 @@ public class FontFamily { /** * @hide + * + * This cannot be deleted because it's in use by AndroidX. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 123768928) public long mNativePtr; // Points native font family builder. Must be zero after freezing this family. private long mBuilderPtr; - @UnsupportedAppUsage + /** + * This cannot be deleted because it's in use by AndroidX. + */ + @UnsupportedAppUsage(trackingBug = 123768928) public FontFamily() { mBuilderPtr = nInitBuilder(null, 0); mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr); } - @UnsupportedAppUsage + /** + * This cannot be deleted because it's in use by AndroidX. + */ + @UnsupportedAppUsage(trackingBug = 123768928) public FontFamily(@Nullable String[] langs, int variant) { final String langsString; if (langs == null || langs.length == 0) { @@ -83,8 +94,10 @@ public class FontFamily { * * @return boolean returns false if some error happens in native code, e.g. broken font file is * passed, etc. + * + * This cannot be deleted because it's in use by AndroidX. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 123768928) public boolean freeze() { if (mBuilderPtr == 0) { throw new IllegalStateException("This FontFamily is already frozen"); @@ -98,7 +111,10 @@ public class FontFamily { return mNativePtr != 0; } - @UnsupportedAppUsage + /** + * This cannot be deleted because it's in use by AndroidX. + */ + @UnsupportedAppUsage(trackingBug = 123768928) public void abortCreation() { if (mBuilderPtr == 0) { throw new IllegalStateException("This FontFamily is already frozen or abandoned"); @@ -107,6 +123,10 @@ public class FontFamily { mBuilderPtr = 0; } + /** + * This cannot be deleted because it's in use by AndroidX. + */ + @UnsupportedAppUsage(trackingBug = 123768928) public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { @@ -128,7 +148,10 @@ public class FontFamily { } } - @UnsupportedAppUsage + /** + * This cannot be deleted because it's in use by AndroidX. + */ + @UnsupportedAppUsage(trackingBug = 123768928) public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { @@ -153,8 +176,10 @@ public class FontFamily { * @param isItalic Whether this font is italic. If the weight is set to 0, this will be resolved * using the OS/2 table in the font. * @return + * + * This cannot be deleted because it's in use by AndroidX. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 123768928) public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic, FontVariationAxis[] axes) { diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index 0787d8518fa5..62647741dcfa 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -716,6 +716,14 @@ public class ImageFormat { public static final int PRIVATE = 0x22; /** + * Compressed HEIC format. + * + * <p>This format defines the HEIC brand of High Efficiency Image File + * Format as described in ISO/IEC 23008-12.</p> + */ + public static final int HEIC = 0x48454946; + + /** * Use this function to retrieve the number of bits per pixel of an * ImageFormat. * @@ -796,6 +804,7 @@ public class ImageFormat { case RAW_DEPTH: case Y8: case DEPTH_JPEG: + case HEIC: return true; } diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl b/graphics/java/android/graphics/Insets.aidl index 7bcebfa08fcb..e65e72d27f49 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl +++ b/graphics/java/android/graphics/Insets.aidl @@ -1,6 +1,6 @@ -/* +/* //device/java/android/android/graphics/Insets.aidl ** -** Copyright 2018, The Android Open Source Project +** 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. @@ -15,6 +15,6 @@ ** limitations under the License. */ -package android.telephony.ims; +package android.graphics; -parcelable RcsThreadQueryContinuationToken; +parcelable Insets; diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java index 8258b575d63f..c64c7893564c 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -18,6 +18,8 @@ package android.graphics; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; /** * An Insets instance holds four integer offsets which describe changes to the four @@ -27,7 +29,7 @@ import android.annotation.Nullable; * Insets are immutable so may be treated as values. * */ -public final class Insets { +public final class Insets implements Parcelable { public static final Insets NONE = new Insets(0, 0, 0, 0); public final int left; @@ -73,7 +75,7 @@ public final class Insets { } /** - * Returns a Rect intance with the appropriate values. + * Returns a Rect instance with the appropriate values. * * @hide */ @@ -168,4 +170,29 @@ public final class Insets { ", bottom=" + bottom + '}'; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(left); + out.writeInt(top); + out.writeInt(right); + out.writeInt(bottom); + } + + public static final Parcelable.Creator<Insets> CREATOR = new Parcelable.Creator<Insets>() { + @Override + public Insets createFromParcel(Parcel in) { + return new Insets(in.readInt(), in.readInt(), in.readInt(), in.readInt()); + } + + @Override + public Insets[] newArray(int size) { + return new Insets[size]; + } + }; } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 73442db1c143..e617c42cc70d 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1279,7 +1279,6 @@ public class Paint { * (content of the render target). * <p /> * Pass null to clear any previous blend mode. - * As a convenience, the parameter passed is also returned. * <p /> * * @see BlendMode diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index d07088b93039..7c9529b8ff71 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -92,7 +92,13 @@ public class Typeface { /** The NORMAL style of the default monospace typeface. */ public static final Typeface MONOSPACE; - @UnsupportedAppUsage + /** + * The default {@link Typeface}s for different text styles. + * Call {@link #defaultFromStyle(int)} to get the default typeface for the given text style. + * It shouldn't be changed for app wide typeface settings. Please use theme and font XML for + * the same purpose. + */ + @UnsupportedAppUsage(trackingBug = 123769446) static Typeface[] sDefaults; /** @@ -125,7 +131,11 @@ public class Typeface { static final Map<String, Typeface> sSystemFontMap; // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API. - @UnsupportedAppUsage + /** + * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. + */ + @UnsupportedAppUsage(trackingBug = 123768928) + @Deprecated static final Map<String, android.graphics.FontFamily[]> sSystemFallbackMap = Collections.emptyMap(); @@ -993,7 +1003,7 @@ public class Typeface { * @deprecated */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 123768928) private static Typeface createFromFamilies(android.graphics.FontFamily[] families) { long[] ptrArray = new long[families.length]; for (int i = 0; i < families.length; i++) { @@ -1019,8 +1029,11 @@ public class Typeface { /** * This method is used by supportlib-v27. + * + * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. */ @UnsupportedAppUsage(trackingBug = 123768395) + @Deprecated private static Typeface createFromFamiliesWithDefault( android.graphics.FontFamily[] families, int weight, int italic) { return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic); @@ -1038,8 +1051,11 @@ public class Typeface { * the first family's font is used. If the first family has multiple fonts, the * closest to the regular weight and upright font is used. * @param families array of font families + * + * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 123768928) + @Deprecated private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families, String fallbackName, int weight, int italic) { android.graphics.fonts.FontFamily[] fallback = SystemFonts.getSystemFallback(fallbackName); diff --git a/graphics/java/android/graphics/drawable/ColorStateListDrawable.java b/graphics/java/android/graphics/drawable/ColorStateListDrawable.java index c0c6a4f424a1..5181d8992be5 100644 --- a/graphics/java/android/graphics/drawable/ColorStateListDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorStateListDrawable.java @@ -157,28 +157,22 @@ public class ColorStateListDrawable extends Drawable implements Drawable.Callbac @Override public void invalidateDrawable(Drawable who) { - final Callback callback = getCallback(); - - if (callback != null) { - callback.invalidateDrawable(who); + if (who == mColorDrawable && getCallback() != null) { + getCallback().invalidateDrawable(this); } } @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { - final Callback callback = getCallback(); - - if (callback != null) { - callback.scheduleDrawable(who, what, when); + if (who == mColorDrawable && getCallback() != null) { + getCallback().scheduleDrawable(this, what, when); } } @Override public void unscheduleDrawable(Drawable who, Runnable what) { - final Callback callback = getCallback(); - - if (callback != null) { - callback.unscheduleDrawable(who, what); + if (who == mColorDrawable && getCallback() != null) { + getCallback().unscheduleDrawable(this, what); } } diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 5d5e40fd3ac3..eb169be06025 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -18,8 +18,8 @@ package android.security; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; -import android.annotation.WorkerThread; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.WorkerThread; import android.app.Activity; import android.app.PendingIntent; import android.app.Service; @@ -29,7 +29,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.net.Uri; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.Looper; import android.os.Process; @@ -38,6 +37,8 @@ import android.os.UserHandle; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyProperties; +import com.android.org.conscrypt.TrustedCertificateStore; + import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.Serializable; @@ -58,8 +59,6 @@ import java.util.concurrent.LinkedBlockingQueue; import javax.security.auth.x500.X500Principal; -import com.android.org.conscrypt.TrustedCertificateStore; - /** * The {@code KeyChain} class provides access to private keys and * their corresponding certificate chains in credential storage. @@ -214,8 +213,8 @@ public final class KeyChain { * * @deprecated Use {@link #ACTION_KEYCHAIN_CHANGED}, {@link #ACTION_TRUST_STORE_CHANGED} or * {@link #ACTION_KEY_ACCESS_CHANGED}. Apps that target a version higher than - * {@link Build.VERSION_CODES#N_MR1} will only receive this broadcast if they register for it - * at runtime. + * {@link android.os.Build.VERSION_CODES#N_MR1} will only receive this broadcast if they + * register for it at runtime. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED"; @@ -532,8 +531,8 @@ public final class KeyChain { } /** - * Returns the {@code PrivateKey} for the requested alias, or null - * if there is no result. + * Returns the {@code PrivateKey} for the requested alias, or null if the alias does not exist + * or the caller has no permission to access it (see note on exceptions below). * * <p> This method may block while waiting for a connection to another process, and must never * be called from the main thread. @@ -541,6 +540,15 @@ public final class KeyChain { * at any time from the main thread, it is safer to rely on a long-lived context such as one * returned from {@link Context#getApplicationContext()}. * + * <p> If the caller provides a valid alias to which it was not granted access, then the + * caller must invoke {@link #choosePrivateKeyAlias} again to get another valid alias + * or a grant to access the same alias. + * <p>On Android versions prior to Q, when a key associated with the specified alias is + * unavailable, the method will throw a {@code KeyChainException} rather than return null. + * If the exception's cause (as obtained by calling {@code KeyChainException.getCause()}) + * is a throwable of type {@code IllegalStateException} then the caller lacks a grant + * to access the key and certificates associated with this alias. + * * @param alias The alias of the desired private key, typically returned via * {@link KeyChainAliasCallback#alias}. * @throws KeyChainException if the alias was valid but there was some problem accessing it. @@ -591,8 +599,10 @@ public final class KeyChain { } /** - * Returns the {@code X509Certificate} chain for the requested - * alias, or null if there is no result. + * Returns the {@code X509Certificate} chain for the requested alias, or null if the alias + * does not exist or the caller has no permission to access it (see note on exceptions + * in {@link #getPrivateKey}). + * * <p> * <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was * installed, this method will return that chain. If only the client certificate was specified @@ -604,6 +614,9 @@ public final class KeyChain { * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed * at any time from the main thread, it is safer to rely on a long-lived context such as one * returned from {@link Context#getApplicationContext()}. + * <p> In case the caller specifies an alias for which it lacks a grant, it must call + * {@link #choosePrivateKeyAlias} again. See {@link #getPrivateKey} for more details on + * coping with this scenario. * * @param alias The alias of the desired certificate chain, typically * returned via {@link KeyChainAliasCallback#alias}. diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 20303eba6667..66d8542553d2 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -203,6 +203,27 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCooki return nullptr; } +const std::unordered_map<std::string, std::string>* + AssetManager2::GetOverlayableMapForPackage(uint32_t package_id) const { + + if (package_id >= package_ids_.size()) { + return nullptr; + } + + const size_t idx = package_ids_[package_id]; + if (idx == 0xff) { + return nullptr; + } + + const PackageGroup& package_group = package_groups_[idx]; + if (package_group.packages_.size() == 0) { + return nullptr; + } + + const auto loaded_package = package_group.packages_[0].loaded_package_; + return &loaded_package->GetOverlayableMap(); +} + void AssetManager2::SetConfiguration(const ResTable_config& configuration) { const int diff = configuration_.diff(configuration); configuration_ = configuration; @@ -704,9 +725,29 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu return cookie; } +const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) { + auto cached_iter = cached_bag_resid_stacks_.find(resid); + if (cached_iter != cached_bag_resid_stacks_.end()) { + return cached_iter->second; + } else { + auto found_resids = std::vector<uint32_t>(); + GetBag(resid, found_resids); + // Cache style stacks if they are not already cached. + cached_bag_resid_stacks_[resid] = found_resids; + return found_resids; + } +} + const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { auto found_resids = std::vector<uint32_t>(); - return GetBag(resid, found_resids); + auto bag = GetBag(resid, found_resids); + + // Cache style stacks if they are not already cached. + auto cached_iter = cached_bag_resid_stacks_.find(resid); + if (cached_iter == cached_bag_resid_stacks_.end()) { + cached_bag_resid_stacks_[resid] = found_resids; + } + return bag; } const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) { diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp index 7708e4340397..3b9a348047ba 100644 --- a/libs/androidfw/DisplayEventDispatcher.cpp +++ b/libs/androidfw/DisplayEventDispatcher.cpp @@ -68,7 +68,7 @@ status_t DisplayEventDispatcher::scheduleVsync() { // Drain all pending events. nsecs_t vsyncTimestamp; - int32_t vsyncDisplayId; + PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", @@ -101,10 +101,11 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { // Drain all pending events, keep the last vsync. nsecs_t vsyncTimestamp; - int32_t vsyncDisplayId; + PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", id=%d, count=%d", + ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d", this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); mWaitingForVsync = false; dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); @@ -114,7 +115,7 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { } bool DisplayEventDispatcher::processPendingEvents( - nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) { + nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) { bool gotVsync = false; DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; ssize_t n; @@ -128,11 +129,11 @@ bool DisplayEventDispatcher::processPendingEvents( // ones. That's fine, we only care about the most recent. gotVsync = true; *outTimestamp = ev.header.timestamp; - *outId = ev.header.id; + *outDisplayId = ev.header.displayId; *outCount = ev.vsync.count; break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected); + dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index bdd47061054a..72873abc6a42 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -598,6 +598,13 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, std::string actor; util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor); + if (loaded_package->overlayable_map_.find(name) != + loaded_package->overlayable_map_.end()) { + LOG(ERROR) << "Multiple <overlayable> blocks with the same name '" << name << "'."; + return {}; + } + loaded_package->overlayable_map_.emplace(name, actor); + // Iterate over the overlayable policy chunks contained within the overlayable chunk data ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size()); while (overlayable_iter.HasNext()) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index f29769b834d1..fc5aa9c7f1b9 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -124,6 +124,9 @@ class AssetManager2 { // This may be nullptr if the APK represented by `cookie` has no resource table. const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + const std::unordered_map<std::string, std::string>* + GetOverlayableMapForPackage(uint32_t package_id) const; + // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. void SetConfiguration(const ResTable_config& configuration); @@ -237,6 +240,8 @@ class AssetManager2 { // resource has been resolved yet. std::string GetLastResourceResolution() const; + const std::vector<uint32_t> GetBagResIdStack(uint32_t resid); + // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. // To iterate over the keys, use the following idiom: @@ -355,6 +360,10 @@ class AssetManager2 { // which involves some calculation. std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; + // Cached set of bag resid stacks for each bag. These are cached because they might be requested + // a number of times for each view during View inspection. + std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_; + // Whether or not to save resource resolution steps bool resource_resolution_logging_enabled_ = false; diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h index bf35aa3c15bb..d2addba61679 100644 --- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h +++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h @@ -37,10 +37,12 @@ private: DisplayEventReceiver mReceiver; bool mWaitingForVsync; - virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) = 0; - virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) = 0; + virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; + virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, + bool connected) = 0; virtual int handleEvent(int receiveFd, int events, void* data); - bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount); + bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, + uint32_t* outCount); }; } diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index b5f4006dbb00..950f5413f550 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -20,6 +20,7 @@ #include <memory> #include <set> #include <vector> +#include <unordered_map> #include <unordered_set> #include "android-base/macros.h" @@ -242,6 +243,10 @@ class LoadedPackage { return defines_overlayable_; } + const std::unordered_map<std::string, std::string>& GetOverlayableMap() const { + return overlayable_map_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); @@ -261,6 +266,7 @@ class LoadedPackage { ByteBucketArray<uint32_t> resource_ids_; std::vector<DynamicPackageEntry> dynamic_package_map_; std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_; + std::unordered_map<std::string, std::string> overlayable_map_; }; // Read-only view into a resource table. This class validates all data diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 447fdf5d306a..40c8e46e4d84 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -71,6 +71,9 @@ class AssetManager2Test : public ::testing::Test { app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); ASSERT_THAT(app_assets_, NotNull()); + + overlayable_assets_ = ApkAssets::Load(GetTestDataPath() + "/overlayable/overlayable.apk"); + ASSERT_THAT(overlayable_assets_, NotNull()); } protected: @@ -83,6 +86,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr<const ApkAssets> appaslib_assets_; std::unique_ptr<const ApkAssets> system_assets_; std::unique_ptr<const ApkAssets> app_assets_; + std::unique_ptr<const ApkAssets> overlayable_assets_; }; TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { @@ -703,4 +707,20 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { EXPECT_EQ("", resultDisabled); } +TEST_F(AssetManager2Test, GetOverlayableMap) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({overlayable_assets_.get()}); + + const auto map = assetmanager.GetOverlayableMapForPackage(0x7f); + ASSERT_NE(nullptr, map); + ASSERT_EQ(2, map->size()); + ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme"); + ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable"); +} + } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index b8d3c6bf92fb..d58e8d20c8aa 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -331,7 +331,7 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.basic"), packages[0]->GetPackageName()); + ASSERT_EQ(std::string("com.android.basic"), packages[0]->GetPackageName()); const auto& loaded_package = packages[0]; auto iter = loaded_package->begin(); @@ -369,6 +369,24 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { ASSERT_EQ(end, iter); } +TEST(LoadedArscTest, GetOverlayableMap) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", + "resources.arsc", &contents)); + + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); + + const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName()); + + const auto map = packages[0]->GetOverlayableMap(); + ASSERT_EQ(2, map.size()); + ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme"); + ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable"); +} + // structs with size fields (like Res_value, ResTable_entry) should be // backwards and forwards compatible (aka checking the size field against // sizeof(Res_value) might not be backwards compatible. diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 4c675133a6c1..cf5d7ce3f738 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -55,9 +55,12 @@ DisplayInfo QueryDisplayInfo() { return sDummyDisplay; } + const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(token == nullptr, + "Failed to get display info because internal display is disconnected"); + DisplayInfo displayInfo; - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); - status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &displayInfo); + status_t status = SurfaceComposerClient::getDisplayInfo(token, &displayInfo); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info, error %d", status); return displayInfo; } diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 2a488378e3a8..76c56609ef47 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -164,14 +164,15 @@ bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* * with reading incorrect data from EGLImage backed SkImage (likely a driver bug). */ sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - SkBudgeted::kYes, bitmap->info()); + SkBudgeted::kYes, bitmap->info(), 0, + kTopLeft_GrSurfaceOrigin, nullptr); // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we // attempt to do the intermediate rendering step in 8888 if (!tmpSurface.get()) { SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType); tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes, - tmpInfo); + tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr); if (!tmpSurface.get()) { ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap"); return false; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 1f24f0e64eab..3904ed20fd77 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -329,7 +329,7 @@ void RenderThread::requestVsync() { bool RenderThread::threadLoop() { setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY); if (gOnStartHook) { - gOnStartHook(); + gOnStartHook("RenderThread"); } initThreadLocals(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index d062dba288fc..b18292820c6b 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -75,7 +75,7 @@ struct VsyncSource { class DummyVsyncSource; -typedef void (*JVMAttachHook)(); +typedef void (*JVMAttachHook)(const char* name); class RenderThread : private ThreadBase { PREVENT_COPY_AND_ASSIGN(RenderThread); diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 92b6cbdfc613..0a54aca4970d 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -37,11 +37,13 @@ static android::DisplayInfo DUMMY_DISPLAY{ 0, // presentationDeadline }; -DisplayInfo getBuiltInDisplay() { +DisplayInfo getInternalDisplay() { #if !HWUI_NULL_GPU DisplayInfo display; - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); - status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display); + const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(token == nullptr, + "Failed to get display info because internal display is disconnected\n"); + status_t status = SurfaceComposerClient::getDisplayInfo(token, &display); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); return display; #else diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 0996f4dc706e..116d4de8090a 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -36,7 +36,7 @@ namespace test { extern DisplayInfo gDisplay; #define dp(x) ((x)*android::uirenderer::test::gDisplay.density) -DisplayInfo getBuiltInDisplay(); +DisplayInfo getInternalDisplay(); class TestContext { public: diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 5fa008b5b4df..0e61899ed58b 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -109,7 +109,7 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { // Switch to the real display - gDisplay = getBuiltInDisplay(); + gDisplay = getInternalDisplay(); Properties::forceDrawFrame = true; TestContext testContext; diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp index 6493d495716e..de10ff1824ee 100644 --- a/libs/hwui/thread/TaskManager.cpp +++ b/libs/hwui/thread/TaskManager.cpp @@ -87,7 +87,7 @@ status_t TaskManager::WorkerThread::readyToRun() { setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND); auto onStartHook = renderthread::RenderThread::getOnStartHook(); if (onStartHook) { - onStartHook(); + onStartHook(mName.c_str()); } return NO_ERROR; diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 0619a9c100dd..905e3039ff88 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -36,7 +36,6 @@ cc_library_shared { srcs: [ ":libincident_aidl", - "proto/android/os/header.proto", "proto/android/os/metadata.proto", "src/IncidentReportArgs.cpp", ], @@ -47,4 +46,4 @@ cc_library_shared { }, export_include_dirs: ["include"], -} +}
\ No newline at end of file diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h index ee1e33c43b89..5e8eac1833ce 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -24,8 +24,6 @@ #include <set> #include <vector> -#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" - namespace android { namespace os { @@ -49,7 +47,7 @@ public: void setAll(bool all); void setDest(int dest); void addSection(int section); - void addHeader(const IncidentHeaderProto& headerProto); + void addHeader(const vector<uint8_t>& headerProto); inline bool all() const { return mAll; } bool containsSection(int section) const; diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index 26261ef929ae..06b7a5b682b1 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -161,15 +161,9 @@ IncidentReportArgs::addSection(int section) } void -IncidentReportArgs::addHeader(const IncidentHeaderProto& headerProto) +IncidentReportArgs::addHeader(const vector<uint8_t>& headerProto) { - vector<uint8_t> header; - auto serialized = headerProto.SerializeAsString(); - if (serialized.empty()) return; - for (auto it = serialized.begin(); it != serialized.end(); it++) { - header.push_back((uint8_t)*it); - } - mHeaders.push_back(header); + mHeaders.push_back(headerProto); } bool diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 602cc3e6d0fd..59eff6401deb 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -17,6 +17,7 @@ package android.location; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -49,6 +50,7 @@ public final class GnssMeasurement implements Parcelable { private double mSnrInDb; private double mAutomaticGainControlLevelInDb; private int mCodeType; + private String mOtherCodeTypeName; // The following enumerations must be in sync with the values declared in gps.h @@ -209,8 +211,8 @@ public final class GnssMeasurement implements Parcelable { */ @IntDef(prefix = { "CODE_TYPE_" }, value = { CODE_TYPE_UNKNOWN, CODE_TYPE_A, CODE_TYPE_B, CODE_TYPE_C, CODE_TYPE_I, CODE_TYPE_L, - CODE_TYPE_M, CODE_TYPE_P, CODE_TYPE_Q, CODE_TYPE_S, CODE_TYPE_W, CODE_TYPE_X, - CODE_TYPE_Y, CODE_TYPE_Z, CODE_TYPE_CODELESS + CODE_TYPE_M, CODE_TYPE_N, CODE_TYPE_P, CODE_TYPE_Q, CODE_TYPE_S, CODE_TYPE_W, + CODE_TYPE_X, CODE_TYPE_Y, CODE_TYPE_Z, CODE_TYPE_OTHER }) @Retention(RetentionPolicy.SOURCE) public @interface CodeType {} @@ -299,7 +301,16 @@ public final class GnssMeasurement implements Parcelable { /** * The GNSS Measurement's code type is one of the following: GPS L1 codeless, GPS L2 codeless. */ - public static final int CODE_TYPE_CODELESS = 13; + public static final int CODE_TYPE_N = 13; + + /** + * Other code type that does not belong to any of the above code types. + * + * This code type is used in the case that the code type being tracked in this measurement, as + * classified by RINEX standards, does not fit into one of the existing enumerated values. When + * this code type is set, the field otherCodeTypeName must specify the new code type. + */ + public static final int CODE_TYPE_OTHER = 255; /** * All the 'Accumulated Delta Range' flags. @@ -349,6 +360,7 @@ public final class GnssMeasurement implements Parcelable { mSnrInDb = measurement.mSnrInDb; mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb; mCodeType = measurement.mCodeType; + mOtherCodeTypeName = measurement.mOtherCodeTypeName; } /** @@ -1175,8 +1187,8 @@ public final class GnssMeasurement implements Parcelable { /** * Gets the GNSS measurement's code type. * - * <p>Similar to the Attribute field described in Rinex 3.03, e.g., in Tables 4-10, and Table - * A2 at the Rinex 3.03 Update 1 Document. + * <p>Similar to the Attribute field described in RINEX 3.03, e.g., in Tables 4-10, and Table + * A2 at the RINEX 3.03 Update 1 Document. */ @CodeType public int getCodeType() { @@ -1206,6 +1218,29 @@ public final class GnssMeasurement implements Parcelable { } /** + * Gets the GNSS measurement's code type name when the code type is {@link #CODE_TYPE_OTHER}. + * + * <p>This is used to specify the observation descriptor defined in GNSS Observation Data File + * Header Section Description in the RINEX standard (Version 3.XX), in cases where the code type + * does not align with an existing Android enumerated value. For example, if a code type "G" is + * added, this string shall be set to "G". + */ + @NonNull + public String getOtherCodeTypeName() { + return mOtherCodeTypeName; + } + + /** + * Sets the GNSS measurement's code type name when the code type is {@link #CODE_TYPE_OTHER}. + * + * @hide + */ + @TestApi + public void setOtherCodeTypeName(@NonNull String otherCodeTypeName) { + mOtherCodeTypeName = otherCodeTypeName; + } + + /** * Gets a string representation of the 'code type'. * * <p>For internal and logging use only. @@ -1226,6 +1261,8 @@ public final class GnssMeasurement implements Parcelable { return "CODE_TYPE_L"; case CODE_TYPE_M: return "CODE_TYPE_M"; + case CODE_TYPE_N: + return "CODE_TYPE_N"; case CODE_TYPE_P: return "CODE_TYPE_P"; case CODE_TYPE_Q: @@ -1240,8 +1277,8 @@ public final class GnssMeasurement implements Parcelable { return "CODE_TYPE_Y"; case CODE_TYPE_Z: return "CODE_TYPE_Z"; - case CODE_TYPE_CODELESS: - return "CODE_TYPE_CODELESS"; + case CODE_TYPE_OTHER: + return "CODE_TYPE_OTHER"; default: return "<Invalid: " + mCodeType + ">"; } @@ -1273,6 +1310,7 @@ public final class GnssMeasurement implements Parcelable { gnssMeasurement.mSnrInDb = parcel.readDouble(); gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble(); gnssMeasurement.mCodeType = parcel.readInt(); + gnssMeasurement.mOtherCodeTypeName = parcel.readString(); return gnssMeasurement; } @@ -1306,6 +1344,7 @@ public final class GnssMeasurement implements Parcelable { parcel.writeDouble(mSnrInDb); parcel.writeDouble(mAutomaticGainControlLevelInDb); parcel.writeInt(mCodeType); + parcel.writeString(mOtherCodeTypeName); } @Override @@ -1384,6 +1423,9 @@ public final class GnssMeasurement implements Parcelable { format, "CodeType", hasCodeType() ? getCodeTypeString() : null)); + builder.append(String.format( + format, + "OtherCodeTypeName", mOtherCodeTypeName)); return builder.toString(); } @@ -1409,6 +1451,7 @@ public final class GnssMeasurement implements Parcelable { resetSnrInDb(); resetAutomaticGainControlLevel(); resetCodeType(); + setOtherCodeTypeName(""); } private void setFlag(int flag) { diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index b9088d417dad..31d22327c79f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -31,6 +31,8 @@ import android.system.OsConstants; import android.util.Log; import android.util.Pair; +import com.android.internal.util.ArrayUtils; + import libcore.io.IoUtils; import libcore.io.Streams; @@ -395,6 +397,12 @@ public class ExifInterface { * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html */ public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw"; + /** + * Type is byte[]. See <a href= + * "https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">Extensible + * Metadata Platform (XMP)</a> for details on contents. + */ + public static final String TAG_XMP = "Xmp"; /** * Private tags used for pointing the other IFD offsets. @@ -1012,7 +1020,8 @@ public class ExifInterface { new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG), new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG), new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT), - new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED) + new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED), + new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE), }; // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels) @@ -1243,6 +1252,8 @@ public class ExifInterface { private static final Charset ASCII = Charset.forName("US-ASCII"); // Identifier for EXIF APP1 segment in JPEG private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII); + // Identifier for XMP APP1 segment in JPEG + private static final byte[] IDENTIFIER_XMP_APP1 = "http://ns.adobe.com/xap/1.0/\0".getBytes(ASCII); // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start // of frame(baseline DCT) and the image size info exists in its beginning part. @@ -2046,6 +2057,22 @@ public class ExifInterface { } /** + * Returns the raw bytes for the value of the requested tag inside the image + * file, or {@code null} if the tag is not contained. + * + * @return raw bytes for the value of the requested tag, or {@code null} if + * no tag was found. + */ + public @Nullable byte[] getAttributeBytes(@NonNull String tag) { + final ExifAttribute attribute = getExifAttribute(tag); + if (attribute != null) { + return attribute.bytes; + } else { + return null; + } + } + + /** * Stores the latitude and longitude value in a float array. The first element is * the latitude, and the second element is the longitude. Returns false if the * Exif tags are not available. @@ -2432,40 +2459,32 @@ public class ExifInterface { } switch (marker) { case MARKER_APP1: { - if (DEBUG) { - Log.d(TAG, "MARKER_APP1"); - } - if (length < 6) { - // Skip if it's not an EXIF APP1 segment. - break; - } - byte[] identifier = new byte[6]; - if (in.read(identifier) != 6) { - throw new IOException("Invalid exif"); - } - bytesRead += 6; - length -= 6; - if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { - // Skip if it's not an EXIF APP1 segment. - break; - } - if (length <= 0) { - throw new IOException("Invalid exif"); - } - if (DEBUG) { - Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")"); - } - // Save offset values for createJpegThumbnailBitmap() function - mExifOffset = bytesRead; - - byte[] bytes = new byte[length]; - if (in.read(bytes) != length) { - throw new IOException("Invalid exif"); - } + final int start = bytesRead; + final byte[] bytes = new byte[length]; + in.readFully(bytes); bytesRead += length; length = 0; - readExifSegment(bytes, imageType); + if (ArrayUtils.startsWith(bytes, IDENTIFIER_EXIF_APP1)) { + final long offset = start + IDENTIFIER_EXIF_APP1.length; + final byte[] value = Arrays.copyOfRange(bytes, + IDENTIFIER_EXIF_APP1.length, bytes.length); + + readExifSegment(value, imageType); + + // Save offset values for createJpegThumbnailBitmap() function + mExifOffset = (int) offset; + } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) { + // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6 + final long offset = start + IDENTIFIER_XMP_APP1.length; + final byte[] value = Arrays.copyOfRange(bytes, + IDENTIFIER_XMP_APP1.length, bytes.length); + + if (getAttribute(TAG_XMP) == null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, new ExifAttribute( + IFD_FORMAT_BYTE, value.length, offset, value)); + } + } break; } diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 26b9b8cf85a7..70a343f4de01 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -155,6 +155,13 @@ public abstract class Image implements AutoCloseable { * UnSupportedOperationException being thrown. * </td> * </tr> + * <tr> + * <td>{@link android.graphics.ImageFormat#HEIC HEIC}</td> + * <td>1</td> + * <td>Compressed data, so row and pixel strides are 0. To uncompress, use + * {@link android.graphics.BitmapFactory#decodeByteArray BitmapFactory#decodeByteArray}. + * </td> + * </tr> * </table> * * @see android.graphics.ImageFormat diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index 60ef1d93191a..6116429ae561 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -821,6 +821,7 @@ public class ImageReader implements AutoCloseable { case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_PRIVATE: case ImageFormat.DEPTH_JPEG: + case ImageFormat.HEIC: width = ImageReader.this.getWidth(); break; default: @@ -838,6 +839,7 @@ public class ImageReader implements AutoCloseable { case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_PRIVATE: case ImageFormat.DEPTH_JPEG: + case ImageFormat.HEIC: height = ImageReader.this.getHeight(); break; default: diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java index b77a884d3412..d8a0bb334c53 100644 --- a/media/java/android/media/ImageUtils.java +++ b/media/java/android/media/ImageUtils.java @@ -36,8 +36,8 @@ class ImageUtils { * {@link android.graphics.PixelFormat PixelFormat} are supported by * ImageReader. When reading RGB data from a surface, the formats defined in * {@link android.graphics.PixelFormat PixelFormat} can be used; when - * reading YUV, JPEG or raw sensor data (for example, from the camera or video - * decoder), formats from {@link android.graphics.ImageFormat ImageFormat} + * reading YUV, JPEG, HEIC or raw sensor data (for example, from the camera + * or video decoder), formats from {@link android.graphics.ImageFormat ImageFormat} * are used. */ public static int getNumPlanesForFormat(int format) { @@ -64,6 +64,7 @@ class ImageUtils { case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_DEPTH: case ImageFormat.DEPTH_JPEG: + case ImageFormat.HEIC: return 1; case ImageFormat.PRIVATE: return 0; @@ -194,6 +195,7 @@ class ImageUtils { case ImageFormat.JPEG: case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.DEPTH_JPEG: + case ImageFormat.HEIC: estimatedBytePerPixel = 0.3; break; case ImageFormat.Y8: @@ -262,6 +264,7 @@ class ImageUtils { case ImageFormat.RAW10: case ImageFormat.RAW12: case ImageFormat.RAW_DEPTH: + case ImageFormat.HEIC: return new Size(image.getWidth(), image.getHeight()); case ImageFormat.PRIVATE: return new Size(0, 0); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 751d57bbdc81..4bc3897c4477 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1614,29 +1614,59 @@ public final class MediaCodecInfo { } /** - * Video performance points are a set of standard performance points defined by pixel rate. + * Video performance points are a set of standard performance points defined by number of + * pixels, pixel rate and frame rate. Performance point represents an upper bound. This + * means that it covers all performance points with fewer pixels, pixel rate and frame + * rate. */ public static final class PerformancePoint { /** - * Frame width in pixels. + * (Maximum) number of macroblocks in the frame. + * + * Video frames are conceptually divided into 16-by-16 pixel blocks called macroblocks. + * Most coding standards operate on these 16-by-16 pixel blocks; thus, codec performance + * is characterized using such blocks. */ - public final int width; + public final int macroBlocks; /** - * Frame height in pixels. + * (Maximum) frame rate in frames per second. */ - public final int height; + public final int frameRate; /** - * Frame rate in frames per second. + * (Maximum) number of macroblocks processed per second. */ - public final int frameRate; + public final long macroBlockRate; /* package private */ - PerformancePoint(int width_, int height_, int frameRate_) { - width = width_; - height = height_; - frameRate = frameRate_; + PerformancePoint(int width_, int height_, int frameRate_, int maxFrameRate_) { + macroBlocks = saturateLongToInt( + ((Math.max(1, (long)width_) + 15) / 16) + * ((Math.max(1, (long)height_) + 15) / 16)); + frameRate = Math.max(1, frameRate_); + macroBlockRate = Math.max(maxFrameRate_, frameRate) * macroBlocks; + } + + /** + * Create a performance point for a given frame size and frame rate. + * + * @param width_ width of the frame in pixels + * @param height_ height of the frame in pixels + * @param frameRate_ frame rate in frames per second + */ + public PerformancePoint(int width_, int height_, int frameRate_) { + this(width_, height_, frameRate_, frameRate_ /* maxFrameRate */); + } + + private int saturateLongToInt(long value) { + if (value < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } else if (value > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else { + return (int)value; + } } /** @@ -1647,26 +1677,40 @@ public final class MediaCodecInfo { * @return {@code true} if the performance point covers the format. */ public boolean covers(@NonNull MediaFormat format) { - // for simplicity, this code assumes a 16x16 block size. - long macroBlocks = ((width + 15) / 16) * (long)((height + 15) / 16); - long mbps = macroBlocks * frameRate; + PerformancePoint other = new PerformancePoint( + format.getInteger(MediaFormat.KEY_WIDTH, 0), + format.getInteger(MediaFormat.KEY_HEIGHT, 0), + // safely convert ceil(double) to int through float case and Math.round + Math.round((float)( + Math.ceil(format.getNumber(MediaFormat.KEY_FRAME_RATE, 0) + .doubleValue())))); + return covers(other); + } - long formatMacroBlocks = - (long)((format.getInteger(MediaFormat.KEY_WIDTH, 0) + 15) / 16) - * ((format.getInteger(MediaFormat.KEY_HEIGHT, 0) + 15) / 16); - double formatMbps = - Math.ceil(formatMacroBlocks - * format.getNumber(MediaFormat.KEY_FRAME_RATE, 0).doubleValue()); - return formatMacroBlocks > 0 && formatMacroBlocks <= macroBlocks - && formatMbps <= mbps; + /** + * Checks whether the performance point covers another performance point. Use this + * method to determine if a performance point advertised by a codec covers the + * performance point required. This method can also be used for lose ordering as this + * method is transitive. + * + * @param other other performance point considered + * + * @return {@code true} if the performance point covers the other. + */ + public boolean covers(@NonNull PerformancePoint other) { + return (macroBlocks >= other.macroBlocks + && frameRate >= other.frameRate + && macroBlockRate >= other.macroBlockRate); } + @Override public boolean equals(Object o) { if (o instanceof PerformancePoint) { PerformancePoint other = (PerformancePoint)o; - return ((long)width * height) == ((long)other.width * other.height) - && frameRate == other.frameRate; + return (macroBlocks == other.macroBlocks + && frameRate == other.frameRate + && macroBlockRate == other.macroBlockRate); } return false; } @@ -1931,7 +1975,8 @@ public final class MediaCodecInfo { continue; } ret.add(new PerformancePoint( - size.getWidth(), size.getHeight(), range.getLower().intValue())); + size.getWidth(), size.getHeight(), range.getLower().intValue(), + range.getUpper().intValue())); } // check if the component specified no performance point indication if (ret.size() == 0) { @@ -1939,9 +1984,12 @@ public final class MediaCodecInfo { } // sort reversed by area first, then by frame rate - ret.sort((a, b) -> (a.width * a.height != b.width * b.height ? - (b.width * b.height - a.width * a.height) : - (b.frameRate - a.frameRate))); + ret.sort((a, b) -> -((a.macroBlocks != b.macroBlocks) ? + (a.macroBlocks < b.macroBlocks ? -1 : 1) : + (a.macroBlockRate != b.macroBlockRate) ? + (a.macroBlockRate < b.macroBlockRate ? -1 : 1) : + (a.frameRate != b.frameRate) ? + (a.frameRate < b.frameRate ? -1 : 1) : 0)); return ret; } @@ -2363,39 +2411,45 @@ public final class MediaCodecInfo { boolean supported = true; switch (profileLevel.level) { case CodecProfileLevel.AVCLevel1: - MBPS = 1485; FS = 99; BR = 64; DPB = 396; break; + MBPS = 1485; FS = 99; BR = 64; DPB = 396; break; case CodecProfileLevel.AVCLevel1b: - MBPS = 1485; FS = 99; BR = 128; DPB = 396; break; + MBPS = 1485; FS = 99; BR = 128; DPB = 396; break; case CodecProfileLevel.AVCLevel11: - MBPS = 3000; FS = 396; BR = 192; DPB = 900; break; + MBPS = 3000; FS = 396; BR = 192; DPB = 900; break; case CodecProfileLevel.AVCLevel12: - MBPS = 6000; FS = 396; BR = 384; DPB = 2376; break; + MBPS = 6000; FS = 396; BR = 384; DPB = 2376; break; case CodecProfileLevel.AVCLevel13: - MBPS = 11880; FS = 396; BR = 768; DPB = 2376; break; + MBPS = 11880; FS = 396; BR = 768; DPB = 2376; break; case CodecProfileLevel.AVCLevel2: - MBPS = 11880; FS = 396; BR = 2000; DPB = 2376; break; + MBPS = 11880; FS = 396; BR = 2000; DPB = 2376; break; case CodecProfileLevel.AVCLevel21: - MBPS = 19800; FS = 792; BR = 4000; DPB = 4752; break; + MBPS = 19800; FS = 792; BR = 4000; DPB = 4752; break; case CodecProfileLevel.AVCLevel22: - MBPS = 20250; FS = 1620; BR = 4000; DPB = 8100; break; + MBPS = 20250; FS = 1620; BR = 4000; DPB = 8100; break; case CodecProfileLevel.AVCLevel3: - MBPS = 40500; FS = 1620; BR = 10000; DPB = 8100; break; + MBPS = 40500; FS = 1620; BR = 10000; DPB = 8100; break; case CodecProfileLevel.AVCLevel31: - MBPS = 108000; FS = 3600; BR = 14000; DPB = 18000; break; + MBPS = 108000; FS = 3600; BR = 14000; DPB = 18000; break; case CodecProfileLevel.AVCLevel32: - MBPS = 216000; FS = 5120; BR = 20000; DPB = 20480; break; + MBPS = 216000; FS = 5120; BR = 20000; DPB = 20480; break; case CodecProfileLevel.AVCLevel4: - MBPS = 245760; FS = 8192; BR = 20000; DPB = 32768; break; + MBPS = 245760; FS = 8192; BR = 20000; DPB = 32768; break; case CodecProfileLevel.AVCLevel41: - MBPS = 245760; FS = 8192; BR = 50000; DPB = 32768; break; + MBPS = 245760; FS = 8192; BR = 50000; DPB = 32768; break; case CodecProfileLevel.AVCLevel42: - MBPS = 522240; FS = 8704; BR = 50000; DPB = 34816; break; + MBPS = 522240; FS = 8704; BR = 50000; DPB = 34816; break; case CodecProfileLevel.AVCLevel5: - MBPS = 589824; FS = 22080; BR = 135000; DPB = 110400; break; + MBPS = 589824; FS = 22080; BR = 135000; DPB = 110400; break; case CodecProfileLevel.AVCLevel51: - MBPS = 983040; FS = 36864; BR = 240000; DPB = 184320; break; + MBPS = 983040; FS = 36864; BR = 240000; DPB = 184320; break; case CodecProfileLevel.AVCLevel52: - MBPS = 2073600; FS = 36864; BR = 240000; DPB = 184320; break; + MBPS = 2073600; FS = 36864; BR = 240000; DPB = 184320; break; + case CodecProfileLevel.AVCLevel6: + MBPS = 4177920; FS = 139264; BR = 240000; DPB = 696320; break; + case CodecProfileLevel.AVCLevel61: + MBPS = 8355840; FS = 139264; BR = 480000; DPB = 696320; break; + case CodecProfileLevel.AVCLevel62: + MBPS = 16711680; FS = 139264; BR = 800000; DPB = 696320; break; default: Log.w(TAG, "Unrecognized level " + profileLevel.level + " for " + mime); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index a76cf1f6ba25..12e02e7b73cd 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1520,6 +1520,7 @@ public final class MediaFormat { * Create a copy of a media format object. */ public MediaFormat(@NonNull MediaFormat other) { + this(); mMap.putAll(other.mMap); } diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 1c6210e0c060..761b62588e5a 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -353,11 +353,6 @@ public class AudioMix { // no route flags set, use default as described in Builder.setRouteFlags(int) mRouteFlags = ROUTE_FLAG_LOOP_BACK; } - // can't do loop back AND render at same time in this implementation - if (mRouteFlags == (ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK)) { - throw new IllegalArgumentException("Unsupported route behavior combination 0x" + - Integer.toHexString(mRouteFlags)); - } if (mFormat == null) { // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate? int rate = AudioSystem.getPrimaryOutputSamplingRate(); @@ -377,11 +372,11 @@ public class AudioMix { throw new IllegalArgumentException("Unsupported device on non-playback mix"); } } else { - if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) { + if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_RENDER) { throw new IllegalArgumentException( "Can't have flag ROUTE_FLAG_RENDER without an audio device"); } - if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_LOOP_BACK) { + if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) { if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) { mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) { diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 406f9dd94bb4..f07f1e8fb62b 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -153,8 +153,12 @@ JMediaCodec::JMediaCodec( if (nameIsType) { mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus); + if (mCodec == nullptr || mCodec->getName(&mNameAtCreation) != OK) { + mNameAtCreation = "(null)"; + } } else { mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus); + mNameAtCreation = name; } CHECK((mCodec != NULL) != (mInitStatus != OK)); } @@ -699,9 +703,8 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const return err; } - // TODO: get alias ScopedLocalRef<jstring> nameObject(env, - env->NewStringUTF(codecInfo->getCodecName())); + env->NewStringUTF(mNameAtCreation.c_str())); ScopedLocalRef<jstring> canonicalNameObject(env, env->NewStringUTF(codecInfo->getCodecName())); diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 0a53f1a0e268..de08550fa3db 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -155,6 +155,7 @@ private: sp<ALooper> mLooper; sp<MediaCodec> mCodec; + AString mNameAtCreation; sp<AMessage> mCallbackNotification; sp<AMessage> mOnFrameRenderedNotification; diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index cf1494296756..6b8f7457eab9 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -24,6 +24,10 @@ #include <media/IMediaCodecList.h> #include <media/MediaCodecInfo.h> +#include <utils/Vector.h> + +#include <vector> + #include "android_runtime/AndroidRuntime.h" #include "jni.h" #include <nativehelper/JNIHelp.h> @@ -31,25 +35,91 @@ using namespace android; -static sp<IMediaCodecList> getCodecList(JNIEnv *env) { - sp<IMediaCodecList> mcl = MediaCodecList::getInstance(); - if (mcl == NULL) { - // This should never happen unless something is really wrong - jniThrowException( - env, "java/lang/RuntimeException", "cannot get MediaCodecList"); +/** + * This object unwraps codec aliases into individual codec infos as the Java interface handles + * aliases in this way. + */ +class JavaMediaCodecListWrapper { +public: + struct Info { + sp<MediaCodecInfo> info; + AString alias; + }; + + const Info getCodecInfo(size_t index) const { + if (index < mInfoList.size()) { + return mInfoList[index]; + } + // return + return Info { nullptr /* info */, "(none)" /* alias */ }; + } + + size_t countCodecs() const { + return mInfoList.size(); } - return mcl; + + sp<IMediaCodecList> getCodecList() const { + return mCodecList; + } + + size_t findCodecByName(AString name) const { + auto it = mInfoIndex.find(name); + return it == mInfoIndex.end() ? -ENOENT : it->second; + } + + JavaMediaCodecListWrapper(sp<IMediaCodecList> mcl) + : mCodecList(mcl) { + size_t numCodecs = mcl->countCodecs(); + for (size_t ix = 0; ix < numCodecs; ++ix) { + sp<MediaCodecInfo> info = mcl->getCodecInfo(ix); + Vector<AString> namesAndAliases; + info->getAliases(&namesAndAliases); + namesAndAliases.insertAt(0); + namesAndAliases.editItemAt(0) = info->getCodecName(); + for (const AString &nameOrAlias : namesAndAliases) { + if (mInfoIndex.count(nameOrAlias) > 0) { + // skip duplicate names or aliases + continue; + } + mInfoIndex.emplace(nameOrAlias, mInfoList.size()); + mInfoList.emplace_back(Info { info, nameOrAlias }); + } + } + } + +private: + sp<IMediaCodecList> mCodecList; + std::vector<Info> mInfoList; + std::map<AString, size_t> mInfoIndex; +}; + +static std::mutex sMutex; +static std::unique_ptr<JavaMediaCodecListWrapper> sListWrapper; + +static const JavaMediaCodecListWrapper *getCodecList(JNIEnv *env) { + std::lock_guard<std::mutex> lock(sMutex); + if (sListWrapper == nullptr) { + sp<IMediaCodecList> mcl = MediaCodecList::getInstance(); + if (mcl == NULL) { + // This should never happen unless something is really wrong + jniThrowException( + env, "java/lang/RuntimeException", "cannot get MediaCodecList"); + } + + sListWrapper.reset(new JavaMediaCodecListWrapper(mcl)); + } + return sListWrapper.get(); } -static sp<MediaCodecInfo> getCodecInfo(JNIEnv *env, jint index) { - sp<IMediaCodecList> mcl = getCodecList(env); - if (mcl == NULL) { +static JavaMediaCodecListWrapper::Info getCodecInfo(JNIEnv *env, jint index) { + const JavaMediaCodecListWrapper *mcl = getCodecList(env); + if (mcl == nullptr) { // Runtime exception already pending. - return NULL; + return JavaMediaCodecListWrapper::Info { nullptr /* info */, "(none)" /* alias */ }; } - sp<MediaCodecInfo> info = mcl->getCodecInfo(index); - if (info == NULL) { + JavaMediaCodecListWrapper::Info info = mcl->getCodecInfo(index); + if (info.info == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); } @@ -58,36 +128,36 @@ static sp<MediaCodecInfo> getCodecInfo(JNIEnv *env, jint index) { static jint android_media_MediaCodecList_getCodecCount( JNIEnv *env, jobject /* thiz */) { - sp<IMediaCodecList> mcl = getCodecList(env); + const JavaMediaCodecListWrapper *mcl = getCodecList(env); if (mcl == NULL) { // Runtime exception already pending. return 0; } + return mcl->countCodecs(); } static jstring android_media_MediaCodecList_getCodecName( JNIEnv *env, jobject /* thiz */, jint index) { - sp<MediaCodecInfo> info = getCodecInfo(env, index); - if (info == NULL) { + JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index); + if (info.info == NULL) { // Runtime exception already pending. return NULL; } - // TODO: support aliases - const char *name = info->getCodecName(); + const char *name = info.alias.c_str(); return env->NewStringUTF(name); } static jstring android_media_MediaCodecList_getCanonicalName( JNIEnv *env, jobject /* thiz */, jint index) { - sp<MediaCodecInfo> info = getCodecInfo(env, index); - if (info == NULL) { + JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index); + if (info.info == NULL) { // Runtime exception already pending. return NULL; } - const char *name = info->getCodecName(); + const char *name = info.info->getCodecName(); return env->NewStringUTF(name); } @@ -104,7 +174,7 @@ static jint android_media_MediaCodecList_findCodecByName( return -ENOENT; } - sp<IMediaCodecList> mcl = getCodecList(env); + const JavaMediaCodecListWrapper *mcl = getCodecList(env); if (mcl == NULL) { // Runtime exception already pending. env->ReleaseStringUTFChars(name, nameStr); @@ -118,25 +188,25 @@ static jint android_media_MediaCodecList_findCodecByName( static jboolean android_media_MediaCodecList_getAttributes( JNIEnv *env, jobject /* thiz */, jint index) { - sp<MediaCodecInfo> info = getCodecInfo(env, index); - if (info == NULL) { + JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index); + if (info.info == NULL) { // Runtime exception already pending. return 0; } - return info->getAttributes(); + return info.info->getAttributes(); } static jarray android_media_MediaCodecList_getSupportedTypes( JNIEnv *env, jobject /* thiz */, jint index) { - sp<MediaCodecInfo> info = getCodecInfo(env, index); - if (info == NULL) { + JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index); + if (info.info == NULL) { // Runtime exception already pending. return NULL; } Vector<AString> types; - info->getSupportedMediaTypes(&types); + info.info->getSupportedMediaTypes(&types); jclass clazz = env->FindClass("java/lang/String"); CHECK(clazz != NULL); @@ -160,8 +230,8 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( return NULL; } - sp<MediaCodecInfo> info = getCodecInfo(env, index); - if (info == NULL) { + JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index); + if (info.info == NULL) { // Runtime exception already pending. return NULL; } @@ -181,7 +251,7 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( // TODO query default-format also from codec/codec list const sp<MediaCodecInfo::Capabilities> &capabilities = - info->getCapabilitiesFor(typeStr); + info.info->getCapabilitiesFor(typeStr); env->ReleaseStringUTFChars(type, typeStr); typeStr = NULL; if (capabilities == NULL) { @@ -192,7 +262,7 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( capabilities->getSupportedColorFormats(&colorFormats); capabilities->getSupportedProfileLevels(&profileLevels); sp<AMessage> details = capabilities->getDetails(); - bool isEncoder = info->isEncoder(); + bool isEncoder = info.info->isEncoder(); jobject defaultFormatObj = NULL; if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) { @@ -267,13 +337,13 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( } static jobject android_media_MediaCodecList_getGlobalSettings(JNIEnv *env, jobject /* thiz */) { - sp<IMediaCodecList> mcl = getCodecList(env); + const JavaMediaCodecListWrapper *mcl = getCodecList(env); if (mcl == NULL) { // Runtime exception already pending. return NULL; } - const sp<AMessage> settings = mcl->getGlobalSettings(); + const sp<AMessage> settings = mcl->getCodecList()->getGlobalSettings(); if (settings == NULL) { jniThrowException(env, "java/lang/RuntimeException", "cannot get global settings"); return NULL; diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index 458d8471dafd..01baadb2f024 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -29,6 +29,9 @@ #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) +// Must be in sync with the value in HeicCompositeStream.cpp +#define CAMERA3_HEIC_BLOB_ID 0x00FE + namespace android { AssetStream::AssetStream(SkStream* stream) @@ -609,34 +612,35 @@ bool isPossiblyYUV(PixelFormat format) { } } -uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride) { +uint32_t Image_getBlobSize(LockedImage* buffer, bool usingRGBAOverride) { ALOGV("%s", __FUNCTION__); LOG_ALWAYS_FATAL_IF(buffer == NULL, "Input buffer is NULL!!!"); uint32_t size = 0; uint32_t width = buffer->width; - uint8_t* jpegBuffer = buffer->data; + uint8_t* blobBuffer = buffer->data; if (usingRGBAOverride) { width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4; } - // First check for JPEG transport header at the end of the buffer - uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob)); + // First check for BLOB transport header at the end of the buffer + uint8_t* header = blobBuffer + (width - sizeof(struct camera3_jpeg_blob)); struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header); - if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) { + if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID || + blob->jpeg_blob_id == CAMERA3_HEIC_BLOB_ID) { size = blob->jpeg_size; - ALOGV("%s: Jpeg size = %d", __FUNCTION__, size); + ALOGV("%s: Jpeg/Heic size = %d", __FUNCTION__, size); } // failed to find size, default to whole buffer if (size == 0) { /* - * This is a problem because not including the JPEG header - * means that in certain rare situations a regular JPEG blob + * This is a problem because not including the JPEG/BLOB header + * means that in certain rare situations a regular JPEG/HEIC blob * will be mis-identified as having a header, in which case * we will get a garbage size value. */ - ALOGW("%s: No JPEG header detected, defaulting to size=width=%d", + ALOGW("%s: No JPEG/HEIC header detected, defaulting to size=width=%d", __FUNCTION__, width); size = width; } @@ -760,7 +764,7 @@ status_t getLockedImageInfo(LockedImage* buffer, int idx, pData = buffer->data; - dataSize = Image_getJpegSize(buffer, usingRGBAOverride); + dataSize = Image_getBlobSize(buffer, usingRGBAOverride); pStride = 0; rStride = 0; break; diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h index 821c6b25c333..19c1b88f78e8 100644 --- a/media/jni/android_media_Utils.h +++ b/media/jni/android_media_Utils.h @@ -119,7 +119,7 @@ bool usingRGBAToJpegOverride(int32_t imageFormat, int32_t containerFormat); int32_t applyFormatOverrides(int32_t imageFormat, int32_t containerFormat); -uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride); +uint32_t Image_getBlobSize(LockedImage* buffer, bool usingRGBAOverride); bool isFormatOpaque(int format); diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp index c3629da1cb12..2db575bf5a13 100644 --- a/native/android/choreographer.cpp +++ b/native/android/choreographer.cpp @@ -24,6 +24,7 @@ #include <android/choreographer.h> #include <androidfw/DisplayEventDispatcher.h> #include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> #include <utils/Looper.h> #include <utils/Mutex.h> #include <utils/Timers.h> @@ -67,8 +68,8 @@ private: explicit Choreographer(const sp<Looper>& looper); Choreographer(const Choreographer&) = delete; - virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); - virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; + void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void scheduleCallbacks(); @@ -139,13 +140,10 @@ void Choreographer::scheduleCallbacks() { } } - -void Choreographer::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t) { - if (id != ISurfaceComposer::eDisplayIdMain) { - ALOGV("choreographer %p ~ ignoring vsync signal for non-main display (id=%d)", this, id); - scheduleVsync(); - return; - } +// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the +// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for +// the internal display implicitly. +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { std::vector<FrameCallback> callbacks{}; { AutoMutex _l{mLock}; @@ -160,9 +158,10 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t) { } } -void Choreographer::dispatchHotplug(nsecs_t, int32_t id, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (id=%" PRId32 ", connected=%s), ignoring.", - this, id, toString(connected)); +void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { + ALOGV("choreographer %p ~ received hotplug event (displayId=%" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.", + this, displayId, toString(connected)); } void Choreographer::handleMessage(const Message& message) { diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 416ef42d21a7..7d2934b8554e 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -46,7 +46,13 @@ using Transaction = SurfaceComposerClient::Transaction; static bool getWideColorSupport(const sp<SurfaceControl>& surfaceControl) { sp<SurfaceComposerClient> client = surfaceControl->getClient(); - sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + + const sp<IBinder> display = client->getInternalDisplayToken(); + if (display == nullptr) { + ALOGE("unable to get wide color support for disconnected internal display"); + return false; + } + bool isWideColorDisplay = false; status_t err = client->isWideColorDisplay(display, &isWideColorDisplay); if (err) { @@ -58,7 +64,12 @@ static bool getWideColorSupport(const sp<SurfaceControl>& surfaceControl) { static bool getHdrSupport(const sp<SurfaceControl>& surfaceControl) { sp<SurfaceComposerClient> client = surfaceControl->getClient(); - sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + + const sp<IBinder> display = client->getInternalDisplayToken(); + if (display == nullptr) { + ALOGE("unable to get hdr capabilities for disconnected internal display"); + return false; + } HdrCapabilities hdrCapabilities; status_t err = client->getHdrCapabilities(display, &hdrCapabilities); diff --git a/packages/CaptivePortalLogin/res/values-gl/strings.xml b/packages/CaptivePortalLogin/res/values-gl/strings.xml index f6f4aea0c639..64195168f76f 100644 --- a/packages/CaptivePortalLogin/res/values-gl/strings.xml +++ b/packages/CaptivePortalLogin/res/values-gl/strings.xml @@ -13,7 +13,7 @@ <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do sitio non coincide co nome que aparece no certificado."</string> <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado caducou."</string> <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aínda non é válido."</string> - <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado ten unha data non-válida."</string> + <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado ten unha data non válida."</string> <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado non é válido."</string> <string name="ssl_error_unknown" msgid="4405203446079465859">"Produciuse un erro descoñecido relacionado co certificado."</string> <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguranza"</string> diff --git a/packages/CarSystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml index 2f720f5aba65..08e16cdbe3b3 100644 --- a/packages/CarSystemUI/res/values/colors_car.xml +++ b/packages/CarSystemUI/res/values/colors_car.xml @@ -26,6 +26,4 @@ <color name="car_user_switcher_add_user_background_color">@color/car_dark_blue_grey_600</color> <color name="car_user_switcher_add_user_add_sign_color">@color/car_body1_light</color> - <!-- colors for volume dialog tint --> - <color name="car_volume_dialog_tint">@color/car_tint</color> </resources> diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java index 85dab57b5f21..10a0ae5a924c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java @@ -554,8 +554,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { // Adding the items which are not coming from the default item. VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); if (volumeItem.defaultItem) { - // Set progress here due to the progress of seekbar may not be updated. - volumeItem.listItem.setProgress(volumeItem.progress); + updateDefaultVolumeItem(volumeItem.listItem); } else { addSeekbarListItem(volumeItem, groupId, 0, null); } @@ -572,8 +571,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { if (!volumeItem.defaultItem) { itr.remove(); } else { - // Set progress here due to the progress of seekbar may not be updated. - seekbarListItem.setProgress(volumeItem.progress); + updateDefaultVolumeItem(seekbarListItem); } } inAnimator = AnimatorInflater.loadAnimator( @@ -595,6 +593,21 @@ public class CarVolumeDialogImpl implements VolumeDialog { mPagedListAdapter.notifyDataSetChanged(); } + private void updateDefaultVolumeItem(SeekbarListItem seekbarListItem){ + VolumeItem volumeItem = findVolumeItem(seekbarListItem); + + // When volume dialog is expanded or collapsed the default list item is never + // reset. Whereas all other list items are removed when the dialog is collapsed and then + // added when the dialog is expanded using {@link CarVolumeDialogImpl#addSeekbarListItem}. + // This sets the progressbar and the tint color of icons for all items other than default + // if they were changed. For default list item it should be done manually here. + int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint); + Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon); + primaryIcon.mutate().setTint(color); + volumeItem.listItem.setPrimaryActionIcon(primaryIcon); + volumeItem.listItem.setProgress(volumeItem.progress); + } + private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener { private final int mVolumeGroupId; diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java b/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java index 7ba0f7ac1d30..f95891cc8962 100644 --- a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java +++ b/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java @@ -15,6 +15,7 @@ */ package android.ext.services.notification; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_MIN; @@ -91,7 +92,7 @@ public class NotificationCategorizer { } // TODO: is from signature app if (entry.getSbn().getUid() < Process.FIRST_APPLICATION_UID) { - if (entry.getImportance() >= IMPORTANCE_HIGH) { + if (entry.getImportance() >= IMPORTANCE_DEFAULT) { return CATEGORY_SYSTEM; } else { return CATEGORY_SYSTEM_LOW; diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index 08cc39fc4935..dc49b13a3a38 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -21,6 +21,7 @@ import android.app.Notification; import android.app.Person; import android.app.RemoteAction; import android.content.Context; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcelable; import android.os.Process; @@ -230,8 +231,11 @@ public class SmartActionsHelper { private Notification.Action createNotificationAction( RemoteAction remoteAction, String actionType) { + Icon icon = remoteAction.shouldShowIcon() + ? remoteAction.getIcon() + : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open); return new Notification.Action.Builder( - remoteAction.getIcon(), + icon, remoteAction.getTitle(), remoteAction.getActionIntent()) .setContextual(true) diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java index c59885e9f4d2..05af6e7a7b71 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java @@ -18,6 +18,7 @@ package android.ext.services.notification; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.os.Process.FIRST_APPLICATION_UID; @@ -172,23 +173,23 @@ public class NotificationCategorizerTest { public void testSystemCategory() { NotificationCategorizer nc = new NotificationCategorizer(); - when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_HIGH)); - when(mEntry.getImportance()).thenReturn(IMPORTANCE_HIGH); + when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT)); + when(mEntry.getImportance()).thenReturn(IMPORTANCE_DEFAULT); when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1); assertEquals(NotificationCategorizer.CATEGORY_SYSTEM, nc.getCategory(mEntry)); assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM)); when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID); - assertEquals(NotificationCategorizer.CATEGORY_HIGH, nc.getCategory(mEntry)); + assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry)); } @Test public void testSystemLowCategory() { NotificationCategorizer nc = new NotificationCategorizer(); - when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT)); - when(mEntry.getImportance()).thenReturn(IMPORTANCE_DEFAULT); + when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_LOW)); + when(mEntry.getImportance()).thenReturn(IMPORTANCE_LOW); when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1); assertEquals(NotificationCategorizer.CATEGORY_SYSTEM_LOW, nc.getCategory(mEntry)); diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java index 4fa7d6462092..923f162c92a6 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java +++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java @@ -23,6 +23,7 @@ import static android.system.OsConstants.ETH_P_ARP; import static android.system.OsConstants.ETH_P_IP; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_RAW; @@ -38,6 +39,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; import android.net.ip.IpClient.IpClientCallbacksWrapper; @@ -55,6 +57,7 @@ import android.system.Os; import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -149,7 +152,9 @@ public class ApfFilter { DROPPED_IPV6_NON_ICMP_MULTICAST, DROPPED_802_3_FRAME, DROPPED_ETHERTYPE_BLACKLISTED, - DROPPED_ARP_REPLY_SPA_NO_HOST; + DROPPED_ARP_REPLY_SPA_NO_HOST, + DROPPED_IPV4_KEEPALIVE_ACK, + DROPPED_IPV6_KEEPALIVE_ACK; // Returns the negative byte offset from the end of the APF data segment for // a given counter. @@ -285,6 +290,7 @@ public class ApfFilter { private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; private static final int IPV4_ANY_HOST_ADDRESS = 0; private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255 + private static final int IPV4_HEADER_LEN = 20; // Without options // Traffic class and Flow label are not byte aligned. Luckily we // don't care about either value so we'll consider bytes 1-3 of the @@ -305,6 +311,8 @@ public class ApfFilter { private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2; private static final int UDP_HEADER_LEN = 8; + private static final int TCP_HEADER_SIZE_OFFSET = 12; + private static final int DHCP_CLIENT_PORT = 68; // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28; @@ -788,7 +796,7 @@ public class ApfFilter { boolean isExpired() { // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll - // have to calculte the filter lifetime specially as a fraction of 0 is still 0. + // have to calculate the filter lifetime specially as a fraction of 0 is still 0. return currentLifetime() <= 0; } @@ -847,11 +855,147 @@ public class ApfFilter { } } + // A class to hold keepalive ack information. + private abstract static class TcpKeepaliveAck { + // Note that the offset starts from IP header. + // These must be added ether header length when generating program. + static final int IP_HEADER_OFFSET = 0; + + protected static class TcpKeepaliveAckData { + public final byte[] srcAddress; + public final int srcPort; + public final byte[] dstAddress; + public final int dstPort; + public final int seq; + public final int ack; + // Create the characteristics of the ack packet from the sent keepalive packet. + TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + srcAddress = sentKeepalivePacket.dstAddress; + srcPort = sentKeepalivePacket.dstPort; + dstAddress = sentKeepalivePacket.srcAddress; + dstPort = sentKeepalivePacket.srcPort; + seq = sentKeepalivePacket.ack; + ack = sentKeepalivePacket.seq + 1; + } + } + + protected final TcpKeepaliveAckData mPacket; + protected final byte[] mSrcDstAddr; + + TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) { + mPacket = packet; + mSrcDstAddr = srcDstAddr; + } + + static byte[] concatArrays(final byte[]... arr) { + int size = 0; + for (byte[] a : arr) { + size += a.length; + } + final byte[] result = new byte[size]; + int offset = 0; + for (byte[] a : arr) { + System.arraycopy(a, 0, result, offset, a.length); + offset += a.length; + } + return result; + } + + public String toString() { + return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d", + mPacket.srcAddress, + mPacket.srcPort, + mPacket.dstAddress, + mPacket.dstPort, + mPacket.seq, + mPacket.ack); + } + + // Append a filter for this keepalive ack to {@code gen}. + // Jump to drop if it matches the keepalive ack. + // Jump to the next filter if packet doesn't match the keepalive ack. + abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException; + } + + private class TcpKeepaliveAckV4 extends TcpKeepaliveAck { + private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12; + private static final int IPV4_TCP_SRC_PORT_OFFSET = 0; + private static final int IPV4_TCP_DST_PORT_OFFSET = 2; + private static final int IPV4_TCP_SEQ_OFFSET = 4; + private static final int IPV4_TCP_ACK_OFFSET = 8; + + TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + this(new TcpKeepaliveAckData(sentKeepalivePacket)); + } + TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) { + super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */); + } + + @Override + void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { + final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked(); + gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addJumpIfR0NotEquals(IPPROTO_TCP, nextFilterLabel); + gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel); + + // Pass the packet if it's not zero-sized : + // Load the IP header size into R1 + gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + // Load the TCP header size into R0 (it's indexed by R1) + gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); + // Size offset is in the top nibble, but it must be multiplied by 4, and the two + // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2. + gen.addRightShift(2); + // R0 += R1 -> R0 contains TCP + IP headers lenght + gen.addAddR1(); + // Add the Ethernet header length to R0. + gen.addLoadImmediate(Register.R1, ETH_HEADER_LEN); + gen.addAddR1(); + // Compare total length of headers to the size of the packet. + gen.addLoadFromMemory(Register.R1, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addNeg(Register.R0); + gen.addAddR1(); + gen.addJumpIfR0NotEquals(0, nextFilterLabel); + + // Add IPv4 header length + gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SRC_PORT_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.srcPort, nextFilterLabel); + gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_DST_PORT_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.dstPort, nextFilterLabel); + gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SEQ_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.seq, nextFilterLabel); + gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_ACK_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.ack, nextFilterLabel); + + maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK); + gen.addJump(mCountAndDropLabel); + gen.defineLabel(nextFilterLabel); + } + } + + private class TcpKeepaliveAckV6 extends TcpKeepaliveAck { + TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + this(new TcpKeepaliveAckData(sentKeepalivePacket)); + } + TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) { + super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */); + } + + @Override + void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { + throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet"); + } + } + // Maximum number of RAs to filter for. private static final int MAX_RAS = 10; @GuardedBy("this") - private ArrayList<Ra> mRas = new ArrayList<Ra>(); + private ArrayList<Ra> mRas = new ArrayList<>(); + @GuardedBy("this") + private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>(); // There is always some marginal benefit to updating the installed APF program when an RA is // seen because we can extend the program's lifetime slightly, but there is some cost to @@ -980,6 +1124,8 @@ public class ApfFilter { // drop // if it's IPv4 broadcast: // drop + // if keepalive ack + // drop // pass if (mMulticastFilter) { @@ -1023,6 +1169,9 @@ public class ApfFilter { gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel); } + // If any keepalive filters, + generateKeepaliveFilter(gen); + // If L2 broadcast packet, drop. // TODO: can we invert this condition to fall through to the common pass case below? maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST); @@ -1030,6 +1179,8 @@ public class ApfFilter { gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); gen.addJump(mCountAndDropLabel); + } else { + generateKeepaliveFilter(gen); } // Otherwise, pass @@ -1037,6 +1188,13 @@ public class ApfFilter { gen.addJump(mCountAndPassLabel); } + private void generateKeepaliveFilter(ApfGenerator gen) throws IllegalInstructionException { + // Drop IPv4 Keepalive acks + for (int i = 0; i < mKeepaliveAcks.size(); ++i) { + final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i); + if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen); + } + } /** * Generate filter code to process IPv6 packets. Execution of this code ends in either the @@ -1057,6 +1215,8 @@ public class ApfFilter { // drop // if it's ICMPv6 NA to ff02::1: // drop + // if keepalive ack + // drop gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); @@ -1112,6 +1272,12 @@ public class ApfFilter { maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); gen.addJump(mCountAndDropLabel); gen.defineLabel(skipUnsolicitedMulticastNALabel); + + // Drop IPv6 Keepalive acks + for (int i = 0; i < mKeepaliveAcks.size(); ++i) { + final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i); + if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen); + } } /** @@ -1489,6 +1655,36 @@ public class ApfFilter { installNewProgramLocked(); } + /** + * Add keepalive ack packet filter. + * This will add a filter to drop acks to the keepalive packet passed as an argument. + * + * @param slot The index used to access the filter. + * @param sentKeepalivePacket The attributes of the sent keepalive packet. + */ + public synchronized void addKeepalivePacketFilter(final int slot, + final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + log("Adding keepalive ack(" + slot + ")"); + if (null != mKeepaliveAcks.get(slot)) { + throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied"); + } + final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6; + mKeepaliveAcks.put(slot, (ipVersion == 4) + ? new TcpKeepaliveAckV4(sentKeepalivePacket) + : new TcpKeepaliveAckV6(sentKeepalivePacket)); + installNewProgramLocked(); + } + + /** + * Remove keepalive packet filter. + * + * @param slot The index used to access the filter. + */ + public synchronized void removeKeepalivePacketFilter(int slot) { + mKeepaliveAcks.remove(slot); + installNewProgramLocked(); + } + static public long counterValue(byte[] data, Counter counter) throws ArrayIndexOutOfBoundsException { // Follow the same wrap-around addressing scheme of the interpreter. @@ -1541,6 +1737,17 @@ public class ApfFilter { } pw.decreaseIndent(); + pw.println("Keepalive filter:"); + pw.increaseIndent(); + for (int i = 0; i < mKeepaliveAcks.size(); ++i) { + final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i); + pw.print("Slot "); + pw.print(mKeepaliveAcks.keyAt(i)); + pw.print(" : "); + pw.println(keepaliveAck); + } + pw.decreaseIndent(); + if (DBG) { pw.println("Last program:"); pw.increaseIndent(); diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java index 87a1b5ea8b4d..809327a0b79d 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java +++ b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java @@ -476,7 +476,7 @@ public class ApfGenerator { /** * Add an instruction to the end of the program to load 16-bits from the packet into - * {@code register}. The offset of the loaded 16-bits from the begining of the packet is + * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ public ApfGenerator addLoad16Indexed(Register register, int offset) { @@ -488,7 +488,7 @@ public class ApfGenerator { /** * Add an instruction to the end of the program to load 32-bits from the packet into - * {@code register}. The offset of the loaded 32-bits from the begining of the packet is + * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ public ApfGenerator addLoad32Indexed(Register register, int offset) { diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 12fe8c507db4..9e5991298834 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -23,6 +23,7 @@ import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission; +import android.annotation.NonNull; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpResults; @@ -34,6 +35,7 @@ import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; import android.net.ProxyInfoParcelable; import android.net.RouteInfo; +import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfCapabilities; import android.net.apf.ApfFilter; import android.net.dhcp.DhcpClient; @@ -292,6 +294,8 @@ public class IpClient extends StateMachine { private static final int EVENT_PROVISIONING_TIMEOUT = 10; private static final int EVENT_DHCPACTION_TIMEOUT = 11; private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12; + private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13; + private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14; // Internal commands to use instead of trying to call transitionTo() inside // a given State's enter() method. Calling transitionTo() from enter/exit @@ -522,6 +526,16 @@ public class IpClient extends StateMachine { checkNetworkStackCallingPermission(); IpClient.this.setMulticastFilter(enabled); } + @Override + public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { + checkNetworkStackCallingPermission(); + IpClient.this.addKeepalivePacketFilter(slot, pkt); + } + @Override + public void removeKeepalivePacketFilter(int slot) { + checkNetworkStackCallingPermission(); + IpClient.this.removeKeepalivePacketFilter(slot); + } } public String getInterfaceName() { @@ -644,6 +658,22 @@ public class IpClient extends StateMachine { } /** + * Called by WifiStateMachine to add keepalive packet filter before setting up + * keepalive offload. + */ + public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) { + sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt); + } + + /** + * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive + * offload. + */ + public void removeKeepalivePacketFilter(int slot) { + sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */); + } + + /** * Dump logs of this IpClient. */ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -1512,6 +1542,23 @@ public class IpClient extends StateMachine { break; } + case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: { + final int slot = msg.arg1; + if (mApfFilter != null) { + mApfFilter.addKeepalivePacketFilter(slot, + (TcpKeepalivePacketDataParcelable) msg.obj); + } + break; + } + + case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: { + final int slot = msg.arg1; + if (mApfFilter != null) { + mApfFilter.removeKeepalivePacketFilter(slot); + } + break; + } + case EVENT_DHCPACTION_TIMEOUT: stopDhcpAction(); break; diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index dbffa6d6bcf2..0d6d080b6dc2 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -61,6 +61,7 @@ import android.net.util.SharedLog; import android.net.util.Stopwatch; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.Bundle; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; @@ -674,11 +675,11 @@ public class NetworkMonitor extends StateMachine { public boolean processMessage(Message message) { switch (message.what) { case CMD_LAUNCH_CAPTIVE_PORTAL_APP: - final Intent intent = new Intent( - ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); + final Bundle appExtras = new Bundle(); // OneAddressPerFamilyNetwork is not parcelable across processes. - intent.putExtra(ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork)); - intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, + appExtras.putParcelable( + ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork)); + appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, new CaptivePortal(new ICaptivePortal.Stub() { @Override public void appResponse(int response) { @@ -700,16 +701,14 @@ public class NetworkMonitor extends StateMachine { } })); final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; - intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); + appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); if (probeRes.probeSpec != null) { final String encodedSpec = probeRes.probeSpec.getEncodedSpec(); - intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec); + appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec); } - intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, + appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, mCaptivePortalUserAgent); - intent.setFlags( - Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + mCm.startCaptivePortalApp(appExtras); return HANDLED; default: return NOT_HANDLED; diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp index 45fa2dc2f383..4a09b3e205a6 100644 --- a/packages/NetworkStack/tests/Android.bp +++ b/packages/NetworkStack/tests/Android.bp @@ -49,6 +49,7 @@ android_test { "libhidlbase", "libhidltransport", "libhwbinder", + "libjsoncpp", "liblog", "liblzma", "libnativehelper", diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java index f76e41217c2a..a4a100000d12 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java @@ -39,13 +39,14 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.SocketKeepalive; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketData.TcpSocketInfo; import android.net.apf.ApfFilter.ApfConfiguration; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; import android.net.ip.IIpClientCallbacks; -import android.net.ip.IpClient; import android.net.ip.IpClient.IpClientCallbacksWrapper; -import android.net.ip.IpClientCallbacks; import android.net.metrics.IpConnectivityLog; import android.net.metrics.RaEvent; import android.net.util.InterfaceParams; @@ -1003,15 +1004,31 @@ public class ApfTest { private static final byte[] ETH_BROADCAST_MAC_ADDRESS = {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; + private static final int IPV4_HEADER_LEN = 20; private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0; + private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2; private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; + private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; + private static final int IPV4_TCP_HEADER_LEN = 20; + private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; + private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0; + private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2; + private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; + private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; + private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12; private static final byte[] IPV4_BROADCAST_ADDRESS = {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; private static final int IPV6_HEADER_LEN = 40; + private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; + private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; + private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0; + private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2; + private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4; + private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8; // The IPv6 all nodes address ff02::1 private static final byte[] IPV6_ALL_NODES_ADDRESS = { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; @@ -1491,6 +1508,200 @@ public class ApfTest { return packet.array(); } + private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5}; + private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6}; + private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7}; + private static final byte[] IPV6_KEEPALIVE_SRC_ADDR = + {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1}; + private static final byte[] IPV6_KEEPALIVE_DST_ADDR = + {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2}; + private static final byte[] IPV6_ANOTHER_ADDR = + {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5}; + + @Test + public void testApfFilterKeepaliveAck() throws Exception { + final MockIpClientCallback cb = new MockIpClientCallback(); + final ApfConfiguration config = getDefaultConfig(); + config.multicastFilter = DROP_MULTICAST; + config.ieee802_3Filter = DROP_802_3_FRAMES; + final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); + byte[] program; + final int srcPort = 12345; + final int dstPort = 54321; + final int seqNum = 2123456789; + final int ackNum = 1234567890; + final int anotherSrcPort = 23456; + final int anotherDstPort = 65432; + final int anotherSeqNum = 2123456780; + final int anotherAckNum = 1123456789; + final int slot1 = 1; + final int slot2 = 2; + final int window = 14480; + final int windowScale = 4; + + // src: 10.0.0.5, port: 12345 + // dst: 10.0.0.6, port: 54321 + InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); + InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); + + final TcpSocketInfo v4Tsi = new TcpSocketInfo( + srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale); + final TcpKeepalivePacketData ipv4TcpKeepalivePacket = + TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi); + + apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable()); + program = cb.getApfProgram(); + + // Verify IPv4 keepalive ack packet is dropped + // src: 10.0.0.6, port: 54321 + // dst: 10.0.0.5, port: 12345 + assertDrop(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); + // Verify IPv4 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */)); + // Verify IPv4 packet from another address is passed + assertPass(program, + ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Remove IPv4 keepalive filter + apfFilter.removeKeepalivePacketFilter(slot1); + + try { + // src: 2404:0:0:0:0:0:faf1, port: 12345 + // dst: 2404:0:0:0:0:0:faf2, port: 54321 + srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR); + dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR); + final TcpSocketInfo v6Tsi = new TcpSocketInfo( + srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale); + final TcpKeepalivePacketData ipv6TcpKeepalivePacket = + TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi); + apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable()); + program = cb.getApfProgram(); + + // Verify IPv6 keepalive ack packet is dropped + // src: 2404:0:0:0:0:0:faf2, port: 54321 + // dst: 2404:0:0:0:0:0:faf1, port: 12345 + assertDrop(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + // Verify IPv6 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum)); + // Verify IPv6 packet from another address is passed + assertPass(program, + ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Remove IPv6 keepalive filter + apfFilter.removeKeepalivePacketFilter(slot1); + + // Verify multiple filters + apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable()); + apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable()); + program = cb.getApfProgram(); + + // Verify IPv4 keepalive ack packet is dropped + // src: 10.0.0.6, port: 54321 + // dst: 10.0.0.5, port: 12345 + assertDrop(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + // Verify IPv4 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum)); + // Verify IPv4 packet from another address is passed + assertPass(program, + ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Verify IPv6 keepalive ack packet is dropped + // src: 2404:0:0:0:0:0:faf2, port: 54321 + // dst: 2404:0:0:0:0:0:faf1, port: 12345 + assertDrop(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + // Verify IPv6 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum)); + // Verify IPv6 packet from another address is passed + assertPass(program, + ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Remove keepalive filters + apfFilter.removeKeepalivePacketFilter(slot1); + apfFilter.removeKeepalivePacketFilter(slot2); + } catch (SocketKeepalive.InvalidPacketException e) { + // TODO: support V6 packets + } + + program = cb.getApfProgram(); + + // Verify IPv4, IPv6 packets are passed + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + assertPass(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + assertPass(program, + ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort, + dstPort, anotherSeqNum, anotherAckNum)); + assertPass(program, + ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort, + dstPort, anotherSeqNum, anotherAckNum)); + + apfFilter.shutdown(); + } + + private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport, + int dport, int seq, int ack) { + ByteBuffer packet = ByteBuffer.wrap(new byte[100]); + packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); + packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); + put(packet, IPV4_SRC_ADDR_OFFSET, sip); + put(packet, IPV4_DEST_ADDR_OFFSET, tip); + packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport); + packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport); + packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq); + packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack); + return packet.array(); + } + + private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport, + int dport, int seq, int ack, int dataLength) { + final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN; + + ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack)); + packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); + // TCP header length 5, reserved 3 bits, NS=0 + packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50); + return packet.array(); + } + + private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport, + int dport, int seq, int ack) { + ByteBuffer packet = ByteBuffer.wrap(new byte[100]); + packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6); + put(packet, IPV6_SRC_ADDR_OFFSET, sip); + put(packet, IPV6_DEST_ADDR_OFFSET, tip); + packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport); + packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport); + packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq); + packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack); + return packet.array(); + } + // Verify that the last program pushed to the IpClient.Callback properly filters the // given packet for the given lifetime. private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) { diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml index 478ee54b40e5..cd6abb267e44 100644 --- a/packages/PrintSpooler/AndroidManifest.xml +++ b/packages/PrintSpooler/AndroidManifest.xml @@ -37,16 +37,8 @@ <uses-permission android:name="android.permission.READ_PRINT_SERVICES" /> <uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" /> - <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" - android:dataSentOffDevice="no" - android:dataSharedWithThirdParty="no" - android:dataUsedForMonetization="no" - android:dataRetentionTime="unlimited"/> - <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" - android:dataSentOffDevice="no" - android:dataSharedWithThirdParty="no" - android:dataUsedForMonetization="no" - android:dataRetentionTime="unlimited"/> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:allowClearUserData="true" diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index dbeee1c4b2ca..26ea6ab2e8be 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -220,4 +220,7 @@ <!-- Default for Settings.Secure.CHARGING_SOUNDS_ENABLED --> <bool name="def_charging_sounds_enabled">true</bool> + + <!-- Default for Settings.Secure.NOTIFICATION_BUBBLES --> + <bool name="def_notification_bubbles">true</bool> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 479c964343c3..6152b8cbd562 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -457,6 +457,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.DEBUG_VIEW_ATTRIBUTES, GlobalSettingsProto.Debug.VIEW_ATTRIBUTES); + dumpSetting(s, p, + Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, + GlobalSettingsProto.Debug.VIEW_ATTRIBUTES_APPLICATION_PACKAGE); p.end(debugToken); final long defaultToken = p.start(GlobalSettingsProto.DEFAULT); @@ -2079,6 +2082,9 @@ class SettingsProtoDumpUtil { Settings.Secure.NOTIFICATION_BADGING, SecureSettingsProto.Notification.BADGING); dumpSetting(s, p, + Settings.Secure.NOTIFICATION_BUBBLES, + SecureSettingsProto.Notification.BUBBLES); + dumpSetting(s, p, Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, SecureSettingsProto.Notification.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 23e5f0eea46d..a7ad2239b402 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3232,7 +3232,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 173; + private static final int SETTINGS_VERSION = 174; private final int mUserId; @@ -4252,6 +4252,24 @@ public class SettingsProvider extends ContentProvider { currentVersion = 173; } + if (currentVersion == 173) { + // Version 173: Set the default value for Secure Settings: NOTIFICATION_BUBBLES + + final SettingsState secureSettings = getSecureSettingsLocked(userId); + + final Setting bubblesSetting = secureSettings.getSettingLocked( + Secure.NOTIFICATION_BUBBLES); + + if (bubblesSetting.isNull()) { + secureSettings.insertSettingLocked(Secure.NOTIFICATION_BUBBLES, + getContext().getResources().getBoolean( + R.bool.def_notification_bubbles) ? "1" : "0", null, + true, SettingsState.SYSTEM_PACKAGE_NAME); + } + + currentVersion = 174; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 866b46f732d8..3778a9c1cf83 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -22,11 +22,6 @@ android:sharedUserId="android.uid.systemui" coreApp="true"> - <!-- Using OpenGL ES 2.0 --> - <uses-feature - android:glEsVersion="0x00020000" - android:required="true" /> - <!-- SysUI must be the one to define this permission; its name is referenced by the core OS. --> <permission android:name="android.permission.systemui.IDENTITY" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java index ac6904300f71..38bf77d5eb0e 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java @@ -55,9 +55,16 @@ public interface ClockPlugin extends Plugin { void setTextColor(int color); /** + * Sets the color palette for the clock face. + * @param supportsDarkText Whether dark text can be displayed. + * @param colors Colors that should be used on the clock face, ordered from darker to lighter. + */ + default void setColorPalette(boolean supportsDarkText, int[] colors) {} + + /** * Notifies that time tick alarm from doze service fired. */ - default void dozeTimeTick() { } + default void dozeTimeTick() {} /** * Set the amount (ratio) that the device has transitioned to doze. diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java new file mode 100644 index 000000000000..3ee69b4c3224 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins.statusbar; + +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; + + +/** + * Sends updates to {@link StateListener}s about changes to the status bar state and dozing state + */ +@ProvidesInterface(version = StatusBarStateController.VERSION) +@DependsOn(target = StatusBarStateController.StateListener.class) +public interface StatusBarStateController { + int VERSION = 1; + + /** + * Current status bar state + */ + int getState(); + + /** + * Is device dozing + */ + boolean isDozing(); + + /** + * Adds a state listener + */ + void addCallback(StateListener listener); + + /** + * Removes callback from listeners + */ + void removeCallback(StateListener listener); + + /** + * Get amount of doze + */ + float getDozeAmount(); + + /** + * Listener for StatusBarState updates + */ + @ProvidesInterface(version = StateListener.VERSION) + public interface StateListener { + int VERSION = 1; + + /** + * Callback before the new state is applied, for those who need to preempt the change. + */ + default void onStatePreChange(int oldState, int newState) { + } + + /** + * Callback after all listeners have had a chance to update based on the state change + */ + default void onStatePostChange() { + } + + /** + * Required callback. Get the new state and do what you will with it. Keep in mind that + * other listeners are typically unordered and don't rely on your work being done before + * other peers. + * + * Only called if the state is actually different. + */ + default void onStateChanged(int newState) { + } + + /** + * Callback to be notified when Dozing changes. Dozing is stored separately from state. + */ + default void onDozingChanged(boolean isDozing) {} + + /** + * Callback to be notified when the doze amount changes. Useful for animations. + * Note: this will be called for each animation frame. Please be careful to avoid + * performance regressions. + */ + default void onDozeAmountChanged(float linear, float eased) {} + } +} diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml index da11db39bd02..54537e5ee7a2 100644 --- a/packages/SystemUI/res-keyguard/values-af/strings.xml +++ b/packages/SystemUI/res-keyguard/values-af/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het <xliff:g id="_NUMBER_1">%d</xliff:g> pogings oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.</item> <item quantity="one">SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het <xliff:g id="_NUMBER_0">%d</xliff:g> poging oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Dit is"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twaalf"</item> <item msgid="7389464214252023751">"Een"</item> diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml index 0d154f552e19..379838dc2a61 100644 --- a/packages/SystemUI/res-keyguard/values-am/strings.xml +++ b/packages/SystemUI/res-keyguard/values-am/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም እስከመጨረሻው መጠቀም የማይቻል ከመሆኑ በፊት <xliff:g id="_NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።</item> <item quantity="other">ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም እስከመጨረሻው መጠቀም የማይቻል ከመሆኑ በፊት <xliff:g id="_NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ነው"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"አስራ ሁለት"</item> <item msgid="7389464214252023751">"አንድ"</item> diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index 826f2f05f2ad..2fef0270b15c 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -182,7 +182,7 @@ <item quantity="other">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item> <item quantity="one">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك محاولة واحدة (<xliff:g id="_NUMBER_0">%d</xliff:g>) قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"الساعة"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"الثانية عشرة"</item> <item msgid="7389464214252023751">"الواحدة"</item> diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml index f67e009ca58e..63d0512ca870 100644 --- a/packages/SystemUI/res-keyguard/values-as/strings.xml +++ b/packages/SystemUI/res-keyguard/values-as/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">ছিমখন অক্ষম হ’ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item> <item quantity="other">ছিমখন অক্ষম হ’ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"সময়"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"বাৰ"</item> <item msgid="7389464214252023751">"এক"</item> diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml index 50f1b1f3d7bc..3714ec6e98ca 100644 --- a/packages/SystemUI/res-keyguard/values-az/strings.xml +++ b/packages/SystemUI/res-keyguard/values-az/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. SIM birdəfəlik yararsız olmadan öncə <xliff:g id="_NUMBER_1">%d</xliff:g> cəhdiniz qalır. Ətraflı məlumat üçün operatorla əlaqə saxlayın.</item> <item quantity="one">SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. SIM birdəfəlik yararsız olmadan öncə <xliff:g id="_NUMBER_0">%d</xliff:g> cəhdiniz qalır. Ətraflı məlumat üçün operatorla əlaqə saxlayın.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Saat"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"On iki"</item> <item msgid="7389464214252023751">"Bir"</item> diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml index a5fceef4f97b..327231305c3c 100644 --- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml @@ -158,7 +158,7 @@ <item quantity="few">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item> <item quantity="other">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Sada je"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"dvanaest"</item> <item msgid="7389464214252023751">"jedan"</item> diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml index 872cd7b997cd..aaa4cb65d37d 100644 --- a/packages/SystemUI/res-keyguard/values-be/strings.xml +++ b/packages/SystemUI/res-keyguard/values-be/strings.xml @@ -166,7 +166,7 @@ <item quantity="many">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спроб, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item> <item quantity="other">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Зараз"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Дванаццаць"</item> <item msgid="7389464214252023751">"Адна"</item> diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml index 741834feca3c..d89dd41c0f72 100644 --- a/packages/SystemUI/res-keyguard/values-bg/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остават ви <xliff:g id="_NUMBER_1">%d</xliff:g> опита, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.</item> <item quantity="one">SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остава ви <xliff:g id="_NUMBER_0">%d</xliff:g> опит, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Часът е"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"дванайсет"</item> <item msgid="7389464214252023751">"един"</item> diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 25346a53a049..d0794c3e2995 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">সিম অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আর <xliff:g id="_NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন, তারপরে এই সিমটি আর একেবারেই ব্যবহার করা যাবে না। বিশদে জানতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।</item> <item quantity="other">সিম অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আর <xliff:g id="_NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন, তারপরে এই সিমটি আর একেবারেই ব্যবহার করা যাবে না। বিশদে জানতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"এখন"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"বারো"</item> <item msgid="7389464214252023751">"এক"</item> diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index 9396ef1ff411..72e4603ba8db 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml @@ -158,7 +158,7 @@ <item quantity="few">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item> <item quantity="other">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Sada je"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dvanaest"</item> <item msgid="7389464214252023751">"Jedan"</item> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index 2fa08975cd8a..185a3f3e39ed 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden <xliff:g id="_NUMBER_1">%d</xliff:g> intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador de telefonia mòbil per obtenir-ne més informació.</item> <item quantity="one">La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queda <xliff:g id="_NUMBER_0">%d</xliff:g> intent; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador de telefonia mòbil per obtenir-ne més informació.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Són les"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"i dotze"</item> <item msgid="7389464214252023751">"Una"</item> diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml index 2708f8f439f8..bb01b7e91852 100644 --- a/packages/SystemUI/res-keyguard/values-cs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml @@ -166,7 +166,7 @@ <item quantity="other">SIM karta je teď zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusů, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item> <item quantity="one">SIM karta je teď zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_0">%d</xliff:g> pokus, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Je"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dvanáct"</item> <item msgid="7389464214252023751">"Jedna"</item> diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml index 34c248d5c6f6..fbdaf0f5eb7e 100644 --- a/packages/SystemUI/res-keyguard/values-da/strings.xml +++ b/packages/SystemUI/res-keyguard/values-da/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.</item> <item quantity="other">SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Den er"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Tolv"</item> <item msgid="7389464214252023751">"Et"</item> diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index d8daf83f34d9..b6127df02f1d 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch <xliff:g id="_NUMBER_1">%d</xliff:g> Versuche, bevor die SIM-Karte endgültig gesperrt wird. Weitere Informationen erhältst du von deinem Mobilfunkanbieter.</item> <item quantity="one">Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch <xliff:g id="_NUMBER_0">%d</xliff:g> Versuch, bevor die SIM-Karte endgültig gesperrt wird. Weitere Informationen erhältst du von deinem Mobilfunkanbieter.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Es ist"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"zwölf Uhr"</item> <item msgid="7389464214252023751">"ein Uhr"</item> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index 6c0916c1d912..402e29765a3a 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Η κάρτα SIM απενεργοποιήθηκε. Καταχωρίστε τον κωδικό PUK, για να συνεχίσετε. Απομένουν <xliff:g id="_NUMBER_1">%d</xliff:g> ακόμη προσπάθειες προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.</item> <item quantity="one">Η κάρτα SIM απενεργοποιήθηκε. Καταχωρίστε τον κωδικό PUK, για να συνεχίσετε. Απομένει <xliff:g id="_NUMBER_0">%d</xliff:g> ακόμη προσπάθεια προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Είναι"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Δώδεκα"</item> <item msgid="7389464214252023751">"Μία"</item> diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml index 80c9b82e984a..72b8085c9da3 100644 --- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item> <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"It’s"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twelve"</item> <item msgid="7389464214252023751">"One"</item> diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml index 20c4d56bf2eb..ecc0f71ffcd5 100644 --- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item> <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"It’s"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twelve"</item> <item msgid="7389464214252023751">"One"</item> diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml index 80c9b82e984a..72b8085c9da3 100644 --- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item> <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"It’s"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twelve"</item> <item msgid="7389464214252023751">"One"</item> diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml index 80c9b82e984a..72b8085c9da3 100644 --- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item> <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"It’s"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twelve"</item> <item msgid="7389464214252023751">"One"</item> diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml index a33d81741256..501dcb755b57 100644 --- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item> <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"It’s"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twelve"</item> <item msgid="7389464214252023751">"One"</item> diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml index 98453a7c2a79..f93d93305621 100644 --- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Se inhabilitó la SIM. Para continuar, ingresa el código PUK. Te quedan <xliff:g id="_NUMBER_1">%d</xliff:g> intentos más antes de que la SIM quede inutilizable permanentemente. Comunícate con tu proveedor para obtener más detalles.</item> <item quantity="one">Se inhabilitó la SIM. Para continuar, ingresa el código PUK. Te queda <xliff:g id="_NUMBER_0">%d</xliff:g> intento más antes de que la SIM quede inutilizable permanentemente. Comunícate con tu proveedor para obtener más detalles.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Son las"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Doce"</item> <item msgid="7389464214252023751">"Una"</item> diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml index 00bdd913a582..9c189d9f1245 100644 --- a/packages/SystemUI/res-keyguard/values-es/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">La tarjeta SIM está inhabilitada. Introduce el código PUK para continuar. Te quedan <xliff:g id="_NUMBER_1">%d</xliff:g> intentos para que la tarjeta SIM quede inservible de forma permanente. Ponte en contacto con tu operador para obtener más información.</item> <item quantity="one">La tarjeta SIM está inhabilitada. Introduce el código PUK para continuar. Te queda <xliff:g id="_NUMBER_0">%d</xliff:g> intento para que la tarjeta SIM quede inservible de forma permanente. Ponte en contacto con tu operador para obtener más información.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Son las"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Doce"</item> <item msgid="7389464214252023751">"Uno"</item> diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml index e77880403b79..759453732db9 100644 --- a/packages/SystemUI/res-keyguard/values-et/strings.xml +++ b/packages/SystemUI/res-keyguard/values-et/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM-kaart on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on jäänud veel <xliff:g id="_NUMBER_1">%d</xliff:g> katset enne, kui SIM-kaart püsivalt lukustatakse. Lisateavet küsige operaatorilt.</item> <item quantity="one">SIM-kaart on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on jäänud veel <xliff:g id="_NUMBER_0">%d</xliff:g> katse enne, kui SIM-kaart püsivalt lukustatakse. Lisateavet küsige operaatorilt.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Kell on"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Kaksteist"</item> <item msgid="7389464214252023751">"Üks"</item> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index 3ebb536d0394..8b1284d7c535 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item> <item quantity="one">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_0">%d</xliff:g> saiakera geratzen zaizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Ordua:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Hamabiak"</item> <item msgid="7389464214252023751">"Ordu bata"</item> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 0c30d03cc14c..183152e37ff8 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">سیمکارت اکنون غیرفعال است. برای ادامه دادن کد PUK را وارد کنید. <xliff:g id="_NUMBER_1">%d</xliff:g> تلاش دیگر باقی مانده است و پس از آن سیمکارت برای همیشه غیرقابلاستفاده میشود. برای اطلاع از جزئیات با شرکت مخابراتی تماس بگیرید.</item> <item quantity="other">سیمکارت اکنون غیرفعال است. برای ادامه دادن کد PUK را وارد کنید. <xliff:g id="_NUMBER_1">%d</xliff:g> تلاش دیگر باقی مانده است و پس از آن سیمکارت برای همیشه غیرقابلاستفاده میشود. برای اطلاع از جزئیات با شرکت مخابراتی تماس بگیرید.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ساعت:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"دوازده"</item> <item msgid="7389464214252023751">"یک"</item> diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml index 60923722fa56..ec3c596f31bf 100644 --- a/packages/SystemUI/res-keyguard/values-fi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM-kortti on nyt lukittu. Anna PUK-koodi, niin voit jatkaa. Sinulla on <xliff:g id="_NUMBER_1">%d</xliff:g> yritystä jäljellä, ennen kuin SIM-kortti poistuu pysyvästi käytöstä. Pyydä lisätietoja operaattoriltasi.</item> <item quantity="one">SIM-kortti on nyt lukittu. Anna PUK-koodi, niin voit jatkaa. Sinulla on <xliff:g id="_NUMBER_0">%d</xliff:g> yritys jäljellä, ennen kuin SIM-kortti poistuu pysyvästi käytöstä. Pyydä lisätietoja operaattoriltasi.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Kello on"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Kaksitoista"</item> <item msgid="7389464214252023751">"Yksi"</item> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index 8e92ffc91db6..73705c876091 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item> <item quantity="other">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Il est"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"douze h."</item> <item msgid="7389464214252023751">"Une h."</item> diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml index cde5e682224f..cf4f2d001d83 100644 --- a/packages/SystemUI/res-keyguard/values-fr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item> <item quantity="other">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Il est"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"douze heures"</item> <item msgid="7389464214252023751">"une heure"</item> diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml index 6d85cdba5ce3..810a75ba034b 100644 --- a/packages/SystemUI/res-keyguard/values-gl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">A SIM está desactivada. Introduce o código PUK para continuar. Quédanche <xliff:g id="_NUMBER_1">%d</xliff:g> intentos antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.</item> <item quantity="one">A SIM está desactivada. Introduce o código PUK para continuar. Quédache <xliff:g id="_NUMBER_0">%d</xliff:g> intento antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Hora:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Doce"</item> <item msgid="7389464214252023751">"Unha"</item> diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml index d9fdf03d4a17..538b10081a83 100644 --- a/packages/SystemUI/res-keyguard/values-gu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">સિમ હવે બંધ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાયમીરૂપે બિનઉપયોગી બની જાય એ પહેલાં તમારી પાસે <xliff:g id="_NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે. વિગતો માટે કૅરિઅરનો સંપર્ક કરો.</item> <item quantity="other">સિમ હવે બંધ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાયમીરૂપે બિનઉપયોગી બની જાય એ પહેલાં તમારી પાસે <xliff:g id="_NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે. વિગતો માટે કૅરિઅરનો સંપર્ક કરો.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"સમય છે"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"બાર"</item> <item msgid="7389464214252023751">"એક"</item> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index b004cdec4045..f03ddd44cb3b 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास <xliff:g id="_NUMBER_1">%d</xliff:g> मौके बचे हैं, उसके बाद, सिम हमेशा के लिए काम करना बंद कर देगा. जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.</item> <item quantity="other">सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास <xliff:g id="_NUMBER_1">%d</xliff:g> मौके बचे हैं, उसके बाद, सिम हमेशा के लिए काम करना बंद कर देगा. जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"यह"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"बारह"</item> <item msgid="7389464214252023751">"एक"</item> diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml index c99b646b7682..28394538d8bb 100644 --- a/packages/SystemUI/res-keyguard/values-hr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml @@ -158,7 +158,7 @@ <item quantity="few">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item> <item quantity="other">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Sada je"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dvanaest"</item> <item msgid="7389464214252023751">"Jedan"</item> diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml index 4eb8bc886d0d..de22c02df0df 100644 --- a/packages/SystemUI/res-keyguard/values-hu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még <xliff:g id="_NUMBER_1">%d</xliff:g> próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.</item> <item quantity="one">A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még <xliff:g id="_NUMBER_0">%d</xliff:g> próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Az idő:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Tizenkettő"</item> <item msgid="7389464214252023751">"Egy"</item> diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml index 5d9a2c0c3fcf..2c104371faa5 100644 --- a/packages/SystemUI/res-keyguard/values-hy/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">SIM քարտն անջատված է: Շարունակելու համար մուտքագրեք PUK կոդը: Մնացել է <xliff:g id="_NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել: Մանրամասների համար դիմեք օպերատորին:</item> <item quantity="other">SIM քարտն անջատված է: Շարունակելու համար մուտքագրեք PUK կոդը: Մնացել է <xliff:g id="_NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել: Մանրամասների համար դիմեք օպերատորին:</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Ժամն է՝"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"տասներկու"</item> <item msgid="7389464214252023751">"մեկ"</item> diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index 603b4c25e9d0..0cd138250fad 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa <xliff:g id="_NUMBER_1">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.</item> <item quantity="one">SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa <xliff:g id="_NUMBER_0">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Pukul"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dua Belas"</item> <item msgid="7389464214252023751">"Satu"</item> diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml index 71f2078a6010..dd9785d976a4 100644 --- a/packages/SystemUI/res-keyguard/values-is/strings.xml +++ b/packages/SystemUI/res-keyguard/values-is/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Það er <xliff:g id="_NUMBER_1">%d</xliff:g> tilraun eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.</item> <item quantity="other">SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Það eru <xliff:g id="_NUMBER_1">%d</xliff:g> tilraunir eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Hún er"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Tólf"</item> <item msgid="7389464214252023751">"Eitt"</item> diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml index e0b55b37fe23..ac2375fd8c34 100644 --- a/packages/SystemUI/res-keyguard/values-it/strings.xml +++ b/packages/SystemUI/res-keyguard/values-it/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.</item> <item quantity="one">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Ora"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dodici"</item> <item msgid="7389464214252023751">"Una"</item> diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml index 509c4637ab92..0fadaffa5c9e 100644 --- a/packages/SystemUI/res-keyguard/values-iw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml @@ -166,7 +166,7 @@ <item quantity="other">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item> <item quantity="one">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותר לך <xliff:g id="_NUMBER_0">%d</xliff:g> ניסיון נוסף לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"השעה"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"שתים-עשרה"</item> <item msgid="7389464214252023751">"אחת"</item> diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index 343d557e50c7..b0514ef4d741 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと <xliff:g id="_NUMBER_1">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。</item> <item quantity="one">SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと <xliff:g id="_NUMBER_0">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"時刻:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"12"</item> <item msgid="7389464214252023751">"1"</item> diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml index 9118e5666d5b..9290874557df 100644 --- a/packages/SystemUI/res-keyguard/values-ka/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM ბარათი ახლა დეაქტივირებულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ <xliff:g id="_NUMBER_1">%d</xliff:g> მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.</item> <item quantity="one">SIM ბარათი ახლა დეაქტივირებულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ <xliff:g id="_NUMBER_0">%d</xliff:g> მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ახლაა"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"თორმეტი"</item> <item msgid="7389464214252023751">"ერთი"</item> diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml index 2270c92da0aa..f1d64499ac45 100644 --- a/packages/SystemUI/res-keyguard/values-kk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. <xliff:g id="_NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.</item> <item quantity="one">SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. <xliff:g id="_NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Қазір"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Он екі"</item> <item msgid="7389464214252023751">"Бір"</item> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 80b124ad47de..f4795b6e79dd 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="_NUMBER_1">%d</xliff:g> ដងទៀតមុនពេលស៊ីមមិនអាចប្រើបានជាអចិន្ត្រៃយ៍។ ទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទសម្រាប់ព័ត៌មានលម្អិត។</item> <item quantity="one">ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="_NUMBER_0">%d</xliff:g> ដងទៀតមុនពេលស៊ីមមិនអាចប្រើបានជាអចិន្ត្រៃយ៍។ ទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទសម្រាប់ព័ត៌មានលម្អិត។</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"វាជា"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"ដប់ពីរ"</item> <item msgid="7389464214252023751">"មួយ"</item> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index f48d6060b2a6..6f83dde04176 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">ಸಿಮ್ ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ಸಿಮ್ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ <xliff:g id="_NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.</item> <item quantity="other">ಸಿಮ್ ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ಸಿಮ್ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ <xliff:g id="_NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ಇದು"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"ಹನ್ನೆರಡು"</item> <item msgid="7389464214252023751">"ಒಂದು"</item> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index 046600911eae..1651be742a3d 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. <xliff:g id="_NUMBER_1">%d</xliff:g>번 더 실패하면 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.</item> <item quantity="one">SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. <xliff:g id="_NUMBER_0">%d</xliff:g>번 더 실패하면 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"현재 시각:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"열두 시"</item> <item msgid="7389464214252023751">"한 시"</item> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index eca9b2214b96..6d0f2ff635dd 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. SIM-картанын биротоло жарактан чыгарына <xliff:g id="_NUMBER_1">%d</xliff:g> аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.</item> <item quantity="one">SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. SIM-картанын биротоло жарактан чыгаарына <xliff:g id="_NUMBER_0">%d</xliff:g> аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Саат"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Он эки"</item> <item msgid="7389464214252023751">"Бир"</item> diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml index bd6e53df924b..23f179e049d4 100644 --- a/packages/SystemUI/res-keyguard/values-lo/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">ຕອນນີ້ປິດການນຳໃຊ້ SIM ແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="_NUMBER_1">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.</item> <item quantity="one">ຕອນນີ້ປິດການນຳໃຊ້ SIM ແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="_NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ມັນແມ່ນ"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"ສິບສອງ"</item> <item msgid="7389464214252023751">"ໜຶ່ງ"</item> diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml index 6df93d8d4838..9e4d169129bc 100644 --- a/packages/SystemUI/res-keyguard/values-lt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml @@ -166,7 +166,7 @@ <item quantity="many">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymo. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item> <item quantity="other">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymų. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Dabar"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dvylika"</item> <item msgid="7389464214252023751">"Pirma"</item> diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml index d9061763e083..9ea4143f2e6e 100644 --- a/packages/SystemUI/res-keyguard/values-lv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml @@ -158,7 +158,7 @@ <item quantity="one">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizi. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item> <item quantity="other">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Laiks:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Divpadsmit"</item> <item msgid="7389464214252023751">"Viens"</item> diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml index 221b99748291..e48b93d4fb46 100644 --- a/packages/SystemUI/res-keyguard/values-mk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостанува уште <xliff:g id="_NUMBER_1">%d</xliff:g> обид пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.</item> <item quantity="other">SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостануваат уште <xliff:g id="_NUMBER_1">%d</xliff:g> обиди пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Сега е"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Дванаесет"</item> <item msgid="7389464214252023751">"Еден"</item> diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index e6642884b343..37ce00710b3f 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി <xliff:g id="_NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു. വിശദാംശങ്ങൾക്ക് കാരിയറുമായി ബന്ധപ്പെടുക.</item> <item quantity="one">സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി <xliff:g id="_NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു. വിശദാംശങ്ങൾക്ക് കാരിയറുമായി ബന്ധപ്പെടുക.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"സമയം"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"പന്ത്രണ്ട്"</item> <item msgid="7389464214252023751">"ഒന്ന്"</item> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index e4187316143a..86da6889249a 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл <xliff:g id="_NUMBER_1">%d</xliff:g> оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.</item> <item quantity="one">SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл <xliff:g id="_NUMBER_0">%d</xliff:g> оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Одоо"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Арван хоёр"</item> <item msgid="7389464214252023751">"Нэг"</item> diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml index 9d83e93b5e68..ea0c0bb7737d 100644 --- a/packages/SystemUI/res-keyguard/values-mr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">सिम आता बंद केलेले आहे. सुरू ठेवण्यासाठी PUK कोड टाका. सिम कायमचे बंद होण्याआधी तुमच्याकडे <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहे. तपशीलांसाठी वाहकाशी संपर्क साधा.</item> <item quantity="other">सिम आता बंद केलेले आहे. सुरू ठेवण्यासाठी PUK कोड टाका. सिम कायमचे बंद होण्याआधी तुमच्याकडे <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत. तपशीलांसाठी वाहकाशी संपर्क साधा.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"हे आहे"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"बारा"</item> <item msgid="7389464214252023751">"एक"</item> diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index 1c82f7c1bda6..03dca91c5d4e 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Tinggal <xliff:g id="_NUMBER_1">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.</item> <item quantity="one">Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Tinggal <xliff:g id="_NUMBER_0">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Pukul"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dua bls"</item> <item msgid="7389464214252023751">"Satu"</item> diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml index efdd779beae6..a468a852d787 100644 --- a/packages/SystemUI/res-keyguard/values-my/strings.xml +++ b/packages/SystemUI/res-keyguard/values-my/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ် အပြီးပိတ်မသွားမီ သင့်တွင် <xliff:g id="_NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item> <item quantity="one">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ် အပြီးပိတ်မသွားမီ သင့်တွင် <xliff:g id="_NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ယခု"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"ဆယ့်နှစ်"</item> <item msgid="7389464214252023751">"တစ်"</item> diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml index 17704c4402d2..fcf06eb7b543 100644 --- a/packages/SystemUI/res-keyguard/values-nb/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.</item> <item quantity="one">SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har <xliff:g id="_NUMBER_0">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Klokken"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Tolv"</item> <item msgid="7389464214252023751">"Ett"</item> diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml index 77bc11337672..c3d92ab18a15 100644 --- a/packages/SystemUI/res-keyguard/values-ne/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item> <item quantity="one">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"समय:"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"बाह्र"</item> <item msgid="7389464214252023751">"एक"</item> diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml index 3bbbb53e2a6b..b3d65bb7e9b4 100644 --- a/packages/SystemUI/res-keyguard/values-nl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">De simkaart is nu uitgeschakeld. Geef de pukcode op om door te gaan. Je hebt nog <xliff:g id="_NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.</item> <item quantity="one">De simkaart is nu uitgeschakeld. Geef de pukcode op om door te gaan. Je hebt nog <xliff:g id="_NUMBER_0">%d</xliff:g> poging over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Het is"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twaalf"</item> <item msgid="7389464214252023751">"Eén"</item> diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml index 0db20aa6881a..94c42c4ccf79 100644 --- a/packages/SystemUI/res-keyguard/values-or/strings.xml +++ b/packages/SystemUI/res-keyguard/values-or/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM କାର୍ଡକୁ ବର୍ତ୍ତମାନ ଅକ୍ଷମ କରିଦିଆଯାଇଛି। ଜାରି ରଖିବାକୁ PUK କୋଡ୍ ଲେଖନ୍ତୁ। ଆଉ <xliff:g id="_NUMBER_1">%d</xliff:g> ଥର ଭୁଲ କୋଡ୍ ଲେଖିବା ପରେ SIM କାର୍ଡ ସ୍ଥାୟୀ ଭାବେ ଅନୁପଯୋଗୀ ହୋଇଯିବ। ବିବରଣୀ ପାଇଁ କେରିଅର୍ର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।</item> <item quantity="one">SIM କାର୍ଡକୁ ବର୍ତ୍ତମାନ ଅକ୍ଷମ କରିଦିଆଯାଇଛି। ଜାରି ରଖିବାକୁ PUK କୋଡ୍ ଲେଖନ୍ତୁ। ଆଉ <xliff:g id="_NUMBER_0">%d</xliff:g> ଥର ଭୁଲ କୋଡ୍ ଲେଖିବା ପରେ SIM କାର୍ଡ ସ୍ଥାୟୀ ଭାବେ ଅନୁପଯୋଗୀ ହୋଇଯିବ। ବିବରଣୀ ପାଇଁ କେରିଅର୍ର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ଏବେ"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"ବାର"</item> <item msgid="7389464214252023751">"ଏକ"</item> diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml index 9fd9b197c25e..9b4e28d36ecc 100644 --- a/packages/SystemUI/res-keyguard/values-pa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item> <item quantity="other">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ਸਮਾਂ"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"ਬਾਰਾਂ"</item> <item msgid="7389464214252023751">"ਇੱਕ"</item> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 106fab363270..229443089359 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -166,7 +166,7 @@ <item quantity="other">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item> <item quantity="one">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_0">%d</xliff:g> próbę, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Jest"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dwanaście"</item> <item msgid="7389464214252023751">"Jeden"</item> diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml index 3c6a37252ba0..56815ca95d19 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item> <item quantity="other">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"São"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Doze"</item> <item msgid="7389464214252023751">"Uma"</item> diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 4f601878e958..4e09dc79d237 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">O SIM encontra-se desativado. Introduza o código PUK para continuar. Tem mais <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.</item> <item quantity="one">O SIM encontra-se desativado. Introduza o código PUK para continuar. Tem mais <xliff:g id="_NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"São"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Doze"</item> <item msgid="7389464214252023751">"Google One"</item> diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml index 3c6a37252ba0..56815ca95d19 100644 --- a/packages/SystemUI/res-keyguard/values-pt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item> <item quantity="other">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"São"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Doze"</item> <item msgid="7389464214252023751">"Uma"</item> diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index 9e4170ec3d6b..23be62ca44f1 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -158,7 +158,7 @@ <item quantity="other">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas <xliff:g id="_NUMBER_1">%d</xliff:g> de încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item> <item quantity="one">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-a mai rămas <xliff:g id="_NUMBER_0">%d</xliff:g> încercare până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Este"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Douăsprezece"</item> <item msgid="7389464214252023751">"Unu"</item> diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml index dd9bc6a80e4d..f093748d852d 100644 --- a/packages/SystemUI/res-keyguard/values-ru/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml @@ -166,7 +166,7 @@ <item quantity="many">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попыток. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item> <item quantity="other">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Сейчас"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"двенадцать"</item> <item msgid="7389464214252023751">"один"</item> diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml index 59d2a067bd40..17122a575612 100644 --- a/packages/SystemUI/res-keyguard/values-si/strings.xml +++ b/packages/SystemUI/res-keyguard/values-si/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">SIM දැන් අබල කර ඇත. දිගටම කරගෙන යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් <xliff:g id="_NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත. විස්තර සඳහා වාහක සම්බන්ධ කර ගන්න.</item> <item quantity="other">SIM දැන් අබල කර ඇත. දිගටම කරගෙන යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් <xliff:g id="_NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත. විස්තර සඳහා වාහක සම්බන්ධ කර ගන්න.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ඒ"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"දොළහ"</item> <item msgid="7389464214252023751">"එක"</item> diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml index 0cd5f78ece65..c9919033af61 100644 --- a/packages/SystemUI/res-keyguard/values-sk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml @@ -166,7 +166,7 @@ <item quantity="other">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusov, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item> <item quantity="one">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_0">%d</xliff:g> pokus, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Práve je"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dvanásť"</item> <item msgid="7389464214252023751">"Jedna"</item> diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml index 52b9d6020ddb..1aba2aae2c71 100644 --- a/packages/SystemUI/res-keyguard/values-sl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml @@ -166,7 +166,7 @@ <item quantity="few">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskuse. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item> <item quantity="other">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskusov. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Ura je"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dvanajst"</item> <item msgid="7389464214252023751">"Ena"</item> diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml index 1dfb8fd8b1db..29819e22df88 100644 --- a/packages/SystemUI/res-keyguard/values-sq/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të kanë mbetur edhe <xliff:g id="_NUMBER_1">%d</xliff:g> përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin për detaje.</item> <item quantity="one">Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të ka mbetur edhe <xliff:g id="_NUMBER_0">%d</xliff:g> përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin për detaje.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Ora është"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Dymbëdhjetë"</item> <item msgid="7389464214252023751">"Një"</item> diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml index 7bb41e9767fb..d572f9692ac7 100644 --- a/packages/SystemUI/res-keyguard/values-sr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml @@ -158,7 +158,7 @@ <item quantity="few">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item> <item quantity="other">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Сада је"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"дванаест"</item> <item msgid="7389464214252023751">"један"</item> diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml index 2475dc1c72b8..6608addcfb19 100644 --- a/packages/SystemUI/res-keyguard/values-sv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. <xliff:g id="_NUMBER_1">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.</item> <item quantity="one">SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. <xliff:g id="_NUMBER_0">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Hon är"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Tolv"</item> <item msgid="7389464214252023751">"En"</item> diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml index 0a1acf2f577b..102db535e5d4 100644 --- a/packages/SystemUI/res-keyguard/values-sw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Umesalia na majaribio <xliff:g id="_NUMBER_1">%d</xliff:g> kabla ya SIM kuacha kufanya kazi kabisa. Wasiliana na mtoa huduma kwa maelezo.</item> <item quantity="one">Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Umesalia na jaribio <xliff:g id="_NUMBER_0">%d</xliff:g> kabla ya SIM kuacha kufanya kazi kabisa. Wasiliana na mtoa huduma kwa maelezo.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Ni saa"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Sita"</item> <item msgid="7389464214252023751">"Saba"</item> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index 57a614a0cfa6..009fb2da2e98 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு, PUK குறியீட்டை உள்ளிடவும். நீங்கள் <xliff:g id="_NUMBER_1">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.</item> <item quantity="one">சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு, PUK குறியீட்டை உள்ளிடவும். நீங்கள் <xliff:g id="_NUMBER_0">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"அதன்"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"பன்னிரண்டு"</item> <item msgid="7389464214252023751">"ஒன்று"</item> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 58880c45cb76..8f4000f84615 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం కారియర్ను సంప్రదించండి.</item> <item quantity="one">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది వివరాల కోసం కారియర్ను సంప్రదించండి.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"ఇప్పుడు"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"పన్నెండు"</item> <item msgid="7389464214252023751">"ఒకటి"</item> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index bb52a7f2bdef..bb58e2092721 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_1">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item> <item quantity="one">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_0">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"เวลา"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"สิบสอง"</item> <item msgid="7389464214252023751">"หนึ่ง"</item> diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml index 1f480f40379f..ec7b924969a3 100644 --- a/packages/SystemUI/res-keyguard/values-tl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">Naka-disable na ang SIM. Ilagay ang PUK code upang magpatuloy. Mayroon kang <xliff:g id="_NUMBER_1">%d</xliff:g> natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.</item> <item quantity="other">Naka-disable na ang SIM. Ilagay ang PUK code upang magpatuloy. Mayroon kang <xliff:g id="_NUMBER_1">%d</xliff:g> na natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Oras ay"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Twelve"</item> <item msgid="7389464214252023751">"One"</item> diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml index 8ff1d88ee83a..92b3fb37d6f7 100644 --- a/packages/SystemUI/res-keyguard/values-tr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce <xliff:g id="_NUMBER_1">%d</xliff:g> deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.</item> <item quantity="one">SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce <xliff:g id="_NUMBER_0">%d</xliff:g> deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Saat"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"On İki"</item> <item msgid="7389464214252023751">"Bir"</item> diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml index 3f0982fe5d47..2772d0738334 100644 --- a/packages/SystemUI/res-keyguard/values-uk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml @@ -166,7 +166,7 @@ <item quantity="many">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроб. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item> <item quantity="other">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Зараз"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"дванадцята"</item> <item msgid="7389464214252023751">"перша"</item> diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 3c520f955b93..e9e67097cfa8 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس <xliff:g id="_NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔</item> <item quantity="one">SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس <xliff:g id="_NUMBER_0">%d</xliff:g> کوشش بچی ہے۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"وقت/ابھی"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"بارہ"</item> <item msgid="7389464214252023751">"ایک"</item> diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml index 640a98760f2f..676f7bb97de6 100644 --- a/packages/SystemUI/res-keyguard/values-uz/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml @@ -152,7 +152,7 @@ <item quantity="other">SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana <xliff:g id="_NUMBER_1">%d</xliff:g> marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun tarmoq operatoriga murojaat qiling.</item> <item quantity="one">SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana <xliff:g id="_NUMBER_0">%d</xliff:g> marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun tarmoq operatoriga murojaat qiling.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Hozir"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Oʻn ikki"</item> <item msgid="7389464214252023751">"Bir"</item> diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml index 2a62fb878231..f2cfb2a2a23e 100644 --- a/packages/SystemUI/res-keyguard/values-vi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn <xliff:g id="_NUMBER_1">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết chi tiết.</item> <item quantity="one">SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn <xliff:g id="_NUMBER_0">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không thể sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết chi tiết.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Hiện là"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Mười hai"</item> <item msgid="7389464214252023751">"Một"</item> diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml index 80fac9030fe0..efa5fc35ff1c 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 <xliff:g id="_NUMBER_1">%d</xliff:g> 次。如果仍不正确,该 SIM 卡将永远无法使用。有关详情,请联系您的运营商。</item> <item quantity="one">SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 <xliff:g id="_NUMBER_0">%d</xliff:g> 次。如果仍不正确,该 SIM 卡将永远无法使用。有关详情,请联系您的运营商。</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"时间是"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"十二"</item> <item msgid="7389464214252023751">"一"</item> diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml index 926d601ebb5a..eeff66acd9a8 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 <xliff:g id="_NUMBER_1">%d</xliff:g> 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請與流動網絡供應商聯絡。</item> <item quantity="one">SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 <xliff:g id="_NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請與流動網絡供應商聯絡。</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"現在是"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"十二"</item> <item msgid="7389464214252023751">"一"</item> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index 728f1a97996f..961ef39f3809 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml @@ -150,7 +150,7 @@ <item quantity="other">SIM 卡現在已遭停用。請輸入 PUK 碼以繼續進行。你還可以再試 <xliff:g id="_NUMBER_1">%d</xliff:g> 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請與電信業者聯絡。</item> <item quantity="one">SIM 卡現在已遭停用。請輸入 PUK 碼以繼續進行。你還可以再試 <xliff:g id="_NUMBER_0">%d</xliff:g> 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請與電信業者聯絡。</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"時間是"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"十二"</item> <item msgid="7389464214252023751">"一"</item> diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml index 7e0a03c02c94..2e948b485276 100644 --- a/packages/SystemUI/res-keyguard/values-zu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml @@ -150,7 +150,7 @@ <item quantity="one">I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-<xliff:g id="_NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasebenziseki unaphakade. Xhumana nenkampani yenethiwekhi ngemininingwane.</item> <item quantity="other">I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-<xliff:g id="_NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasebenziseki unaphakade. Xhumana nenkampani yenethiwekhi ngemininingwane.</item> </plurals> - <string name="type_clock_header" msgid="4786545441902447636">"Kuyi-"</string> + <!-- no translation found for type_clock_header (6782840450655632763) --> <string-array name="type_clock_hours"> <item msgid="3543074812389379830">"Ishumi nambili"</item> <item msgid="7389464214252023751">"Kunye"</item> diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml index 36d06591460b..f1158ef11ccc 100644 --- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml +++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml @@ -17,6 +17,7 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#4a4a4a" /> - <padding android:padding="@dimen/ongoing_appops_chip_bg_padding" /> + <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding" + android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding"/> <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml index 403d92869fbf..f664c0581d7e 100644 --- a/packages/SystemUI/res/layout/bubble_expanded_view.xml +++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<com.android.systemui.bubbles.BubbleExpandedViewContainer +<com.android.systemui.bubbles.BubbleExpandedView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -93,4 +93,4 @@ </FrameLayout> -</com.android.systemui.bubbles.BubbleExpandedViewContainer> +</com.android.systemui.bubbles.BubbleExpandedView> diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml index cbdd51b24388..58fe81109731 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml @@ -29,14 +29,25 @@ android:orientation="horizontal" android:paddingStart="@dimen/ongoing_appops_chip_side_padding" android:paddingEnd="@dimen/ongoing_appops_chip_side_padding" - android:background="@drawable/privacy_chip_bg" android:focusable="true"> + <TextView + android:id="@+id/in_use_text" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical|start" + android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed" + android:gravity="center_vertical" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:textColor="@color/status_bar_clock_color" + android:text="@string/ongoing_privacy_chip_in_use" + /> + <LinearLayout android:id="@+id/icons_container" android:layout_height="match_parent" android:layout_width="wrap_content" - android:layout_gravity="center_vertical|start" + android:layout_gravity="center_vertical" android:gravity="center_vertical" /> @@ -51,7 +62,7 @@ android:gravity="center_vertical" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:textColor="@color/status_bar_clock_color" - android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin" - android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin" + android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin_collapsed" + android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed" /> </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl deleted file mode 100644 index 11d73a93ad04..000000000000 --- a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl +++ /dev/null @@ -1,33 +0,0 @@ -precision mediump float; - -uniform sampler2D uTexture; -uniform float uCenterReveal; -uniform float uReveal; -uniform float uAod2Opacity; -uniform int uAodMode; -varying vec2 vTextureCoordinates; - -vec3 luminosity(vec3 color) { - float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; - return vec3(lum); -} - -vec4 transform(vec3 diffuse) { - // TODO: Add well comments here, tracking on b/123615467. - vec3 lum = luminosity(diffuse); - diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal)); - float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal)); - diffuse = smoothstep(val, 1.0, diffuse); - diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal)); - return vec4(diffuse.r, diffuse.g, diffuse.b, 1.); -} - -void main() { - vec4 fragColor = texture2D(uTexture, vTextureCoordinates); - // TODO: Remove the branch logic here, tracking on b/123615467. - if (uAodMode != 0) { - gl_FragColor = transform(fragColor.rgb); - } else { - gl_FragColor = fragColor; - } -}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl deleted file mode 100644 index 4393e2bb0ebf..000000000000 --- a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl +++ /dev/null @@ -1,8 +0,0 @@ -attribute vec4 aPosition; -attribute vec2 aTextureCoordinates; -varying vec2 vTextureCoordinates; - -void main() { - vTextureCoordinates = aTextureCoordinates; - gl_Position = aPosition; -}
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 053371a0e527..9b765f824859 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> program gebruik jou <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Het dit"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privaatheidsinstellings"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Program wat jou <xliff:g id="TYPES_LIST">%s</xliff:g> gebruik"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Programme wat jou <xliff:g id="TYPES_LIST">%s</xliff:g> gebruik"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors is af"</string> <string name="device_services" msgid="1191212554435440592">"Toesteldienste"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Titelloos"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Maak <xliff:g id="APP_NAME">%1$s</xliff:g> oop"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Maak kennisgewinginstellings oop vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 8acd1b832ed3..d12ba4ca08a4 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> መተግበሪያዎች የእርስዎን <xliff:g id="TYPE_5">%2$s</xliff:g> እየተጠቀሙ ነው።</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"ገባኝ"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"የግላዊነት ቅንብሮች"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> የሚጠቀሙ መተግበሪያዎች"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> የሚጠቀሙ መተግበሪያዎች"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"፣ "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"የማስታወቂያ ቅንብሮች ለ <xliff:g id="APP_NAME">%1$s</xliff:g> ክፈት"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 0312adf36f23..7c08783e365d 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -907,8 +907,7 @@ <item quantity="one">هناك تطبيق واحد (<xliff:g id="NUM_APPS_0">%1$d</xliff:g>) يستخدِم <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"حسنًا"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"إعدادات الخصوصية"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"التطبيق الذي يستخدِم <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"التطبيقات التي تستخدِم <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"، "</string> @@ -927,8 +926,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"فتح إعدادات الإشعارات في <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index fbaad96d0454..c782d1be3da9 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 70608a17531d..896fe3408235 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> tətbiq <xliff:g id="TYPE_1">%2$s</xliff:g> tətbiqindən istifadə edir.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Anladım"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Məxfilik ayarları"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> tətbiqindən istifadə edən tətbiq"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g> tətbiqindən istifadə edən tətbiqlər"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorlar deaktivdir"</string> <string name="device_services" msgid="1191212554435440592">"Cihaz Xidmətləri"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Başlıq yoxdur"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqini açın"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş ayarlarını açın"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 8a754f3425fc..14ab0a4422b0 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -886,8 +886,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> aplikacija koristi dozvolu <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Važi"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Podešav. privatnosti"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacija koja koristi dozvole <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije koje koriste dozvole <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -903,8 +902,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori su isključeni"</string> <string name="device_services" msgid="1191212554435440592">"Usluge za uređaje"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Bez naslova"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Otvorite <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Otvorite podešavanja obaveštenja za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 3390fcbf8ad7..0e724fa02954 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -917,4 +917,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 00454ddda7e3..c44b3ad957ee 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> приложение използва <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Разбрах"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Поверит.: Настройки"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Приложение, което използва <xliff:g id="TYPES_LIST">%s</xliff:g> ви"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Приложения, които използват <xliff:g id="TYPES_LIST">%s</xliff:g> ви"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"Отваряне на настройките за известията за „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index b864206c6084..ec7ff6d7d110 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index d1528316b0ae..77069b2c4e2b 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -888,8 +888,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> aplikacija koristi <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Razumijem"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Postavke privatnosti"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacija koristi odobrenja <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije koriste odobrenja <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -905,8 +904,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori su isključeni"</string> <string name="device_services" msgid="1191212554435440592">"Usluge uređaja"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Bez naslova"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Otvorite aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Otvorite postavke obavijesti za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index f41ea3cd5b83..1c52610722cb 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 4bdc271df52b..735b476a4583 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -915,4 +915,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 0c3c253434da..615338dbcef3 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> apps anvender din/dit <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privatlivsindstill."</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App, der anvender din/dit <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps, der anvender din/dit <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Deaktiver sensorer"</string> <string name="device_services" msgid="1191212554435440592">"Enhedstjenester"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Ingen titel"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Åbn <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Åbn notifikationsindstillingerne for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index c21288dbfadc..4e2e1e134905 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -441,7 +441,7 @@ <string name="battery_saver_notification_text" msgid="820318788126672692">"Reduzierung der Leistung und Hintergrunddaten"</string> <string name="battery_saver_notification_action_text" msgid="132118784269455533">"Energiesparmodus deaktivieren"</string> <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> nimmt alle auf deinem Bildschirm angezeigten Aktivitäten auf."</string> - <string name="media_projection_remember_text" msgid="3103510882172746752">"Nicht erneut anzeigen"</string> + <string name="media_projection_remember_text" msgid="3103510882172746752">"Nicht mehr anzeigen"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Alle löschen"</string> <string name="manage_notifications_text" msgid="2386728145475108753">"Verwalten"</string> <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Benachrichtigungen durch \"Bitte nicht stören\" pausiert"</string> @@ -883,8 +883,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> App verwendet gerade: <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Datenschutzeinst."</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App, die <xliff:g id="TYPES_LIST">%s</xliff:g> verwendet"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps, die <xliff:g id="TYPES_LIST">%s</xliff:g> verwenden"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -899,8 +898,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensoren aus"</string> <string name="device_services" msgid="1191212554435440592">"Gerätedienste"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Kein Titel"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"<xliff:g id="APP_NAME">%1$s</xliff:g> öffnen"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> öffnen"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 23818956b18e..1a0df6bcbda3 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> εφαρμογή χρησιμοποιεί το <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Κατάλαβα"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Ρυθμίσεις απορρήτου"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Εφαρμογή που χρησιμοποιεί τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Εφαρμογές που χρησιμοποιούν τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"Άνοιγμα ρυθμίσεων ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 63f677319abd..95c1349b76e4 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> application is using your <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Got it"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privacy settings"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string> <string name="device_services" msgid="1191212554435440592">"Device Services"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Open <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Open notification settings for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index d6539e7bcd20..6d55c5a6227a 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> application is using your <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Got it"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privacy settings"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string> <string name="device_services" msgid="1191212554435440592">"Device Services"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Open <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Open notification settings for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 63f677319abd..95c1349b76e4 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> application is using your <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Got it"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privacy settings"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string> <string name="device_services" msgid="1191212554435440592">"Device Services"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Open <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Open notification settings for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 63f677319abd..95c1349b76e4 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> application is using your <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Got it"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privacy settings"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string> <string name="device_services" msgid="1191212554435440592">"Device Services"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Open <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Open notification settings for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 36ef6479415e..266faa85f960 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> application is using your <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Got it"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privacy settings"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string> <string name="device_services" msgid="1191212554435440592">"Device Services"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Open <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Open notification settings for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 81cd300fa200..b1f7068b5876 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplicación está usando tu <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Entendido"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Config. privacidad"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Una app está usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Se desactivaron los sensores"</string> <string name="device_services" msgid="1191212554435440592">"Servicios del dispositivo"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Sin título"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Abrir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Abrir la configuración de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 16c182b97d18..b1947fdc46b7 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index b02e45746eb6..8085224caaee 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> rakendus kasutab üksust <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Selge"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privaatsusseaded"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Rakendus, mis kasutab üksusi <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Rakendused, mis kasutavad üksusi <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Andurid on välja lülitatud"</string> <string name="device_services" msgid="1191212554435440592">"Seadme teenused"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Pealkiri puudub"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Ava <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Ava rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguandeseaded"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b2d29c12faaf..7f9bffa37c8d 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplikazio ari da <xliff:g id="TYPE_1">%2$s</xliff:g> erabiltzen.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Ados"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Pribatutasun-ezarpenak"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari den aplikazioa"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari diren aplikazioak"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sentsoreak desaktibatuta daude"</string> <string name="device_services" msgid="1191212554435440592">"Gailuetarako zerbitzuak"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Ez du izenik"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Ireki <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Ireki <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpen-ezarpenak"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 4fb889bc2501..018d6d1fd0da 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> برنامه درحال استفاده از <xliff:g id="TYPE_5">%2$s</xliff:g> شما است.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"متوجه شدم"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"تنظیمات حریم خصوصی"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"برنامهای که از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکند"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"برنامههایی که از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکنند"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"، "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"تنظیمات اعلان <xliff:g id="APP_NAME">%1$s</xliff:g> را باز کنید"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 1ab48a0855d5..0258ccbf9332 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="TYPE_1">%2$s</xliff:g> on <xliff:g id="NUM_APPS_0">%1$d</xliff:g> sovelluksen käytössä.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Selvä"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Tietosuoja-asetukset"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Sovellus, jolla on <xliff:g id="TYPES_LIST">%s</xliff:g> ‑käyttöoikeus"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Sovellukset, joilla on <xliff:g id="TYPES_LIST">%s</xliff:g> ‑käyttöoikeus"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Anturit pois päältä"</string> <string name="device_services" msgid="1191212554435440592">"Laitepalvelut"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Ei nimeä"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Avaa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Avaa ilmoitusasetukset (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 554a22253d93..a3a31356fcfc 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 2e4700059fa3..d1ce32e3a193 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 90b9f87a12af..d76c34a64042 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplicación utiliza o teu dispositivo (<xliff:g id="TYPE_1">%2$s</xliff:g>).</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"De acordo"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Config. privacidade"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicación que utiliza o seguinte: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicacións que utilizan o seguinte: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Desactivar sensores"</string> <string name="device_services" msgid="1191212554435440592">"Servizos do dispositivo"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Sen título"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Abre a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Abre a configuración de notificacións para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 5717616b706d..c84051cc51bb 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 20b9198e6e21..157f55c9bede 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 79f410a87929..77c3ce3b47b8 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -886,8 +886,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> aplikacija upotrebljava <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Shvaćam"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Postavke privatnosti"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacije koje upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije koje upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -903,8 +902,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori su isključeni"</string> <string name="device_services" msgid="1191212554435440592">"Usluge uređaja"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Bez naslova"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Otvorite aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Otvorite postavke obavijesti za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index d0cdfb4601d6..308295a20cc1 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> alkalmazás használja a következőt: <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Értem"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Adatvédelmi beállítások"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"A következőket használó alkalmazás: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"A következőt használó alkalmazások: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Érzékelők kikapcsolva"</string> <string name="device_services" msgid="1191212554435440592">"Eszközszolgáltatások"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Nincs cím"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"<xliff:g id="APP_NAME">%1$s</xliff:g> megnyitása"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> – az alkalmazás értesítési beállításainak megnyitása"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 41bd378e912e..a3e05ad1cc01 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 98e1c4b3d9a3..2bad55e03d62 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplikasi menggunakan <xliff:g id="TYPE_1">%2$s</xliff:g> Anda.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Oke"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Setelan privasi"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikasi yang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> Anda"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikasi yang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> Anda"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensor nonaktif"</string> <string name="device_services" msgid="1191212554435440592">"Layanan Perangkat"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Tanpa judul"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Buka <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Buka setelan notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index d1ea979f5a2e..35026134445e 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> forrit eru að nota <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Ég skil"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Persónuvernd"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Forrit sem nota <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Forrit sem nota <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Slökkt á skynjurum"</string> <string name="device_services" msgid="1191212554435440592">"Tækjaþjónusta"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Enginn titill"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Opna <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Opna tilkynningastillingar fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index af22c6d19399..42ff72a59014 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> applicazione sta utilizzando <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Impostazioni privacy"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App che usa <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"App che utilizzano <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensori disattivati"</string> <string name="device_services" msgid="1191212554435440592">"Servizi del dispositivo"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Senza titolo"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Apri <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Apri le impostazioni di notifica dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index a7d5138cebb7..036b0dfa7f3b 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -519,9 +519,9 @@ <string name="accessibility_volume_collapse" msgid="3609549593031810875">"כווץ"</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> + <string name="screen_pinning_description" msgid="8909878447196419623">"נשאר בתצוגה עד לביטול ההצמדה. יש ללחוץ לחיצה ארוכה על הלחצנים \'הקודם\' ו\'סקירה\' כדי לבטל את ההצמדה."</string> <string name="screen_pinning_description_recents_invisible" msgid="8281145542163727971">"נשאר בתצוגה עד לביטול ההצמדה. יש ללחוץ לחיצה ארוכה על הלחצנים \'הקודם\' ו\'דף הבית\' כדי לבטל את ההצמדה."</string> - <string name="screen_pinning_description_accessible" msgid="426190689254018656">"נשאר בתצוגה עד לביטול ההצמדה. גע בלחצן \'סקירה\' והחזק כדי לבטל את ההצמדה."</string> + <string name="screen_pinning_description_accessible" msgid="426190689254018656">"נשאר בתצוגה עד לביטול ההצמדה. יש ללחוץ לחיצה ארוכה על הלחצן \'סקירה\' כדי לבטל את ההצמדה."</string> <string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"נשאר בתצוגה עד לביטול ההצמדה. יש ללחוץ לחיצה ארוכה על הלחצן \'דף הבית\' כדי לבטל את ההצמדה."</string> <string name="screen_pinning_toast" msgid="2266705122951934150">"כדי לבטל את ההצמדה של מסך זה, יש ללחוץ לחיצה ארוכה על הלחצנים \'הקודם\' ו\'סקירה\'"</string> <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"כדי לבטל את ההצמדה של מסך זה, יש ללחוץ לחיצה ארוכה על הלחצנים \'הקודם\' ו\'דף הבית\'"</string> @@ -915,4 +915,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 9095fd1a8cdd..7aa7fbf39661 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> 個のアプリが <xliff:g id="TYPE_1">%2$s</xliff:g> を使用しています。</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"プライバシー設定"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しているアプリ"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しているアプリ"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"センサー OFF"</string> <string name="device_services" msgid="1191212554435440592">"デバイス サービス"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"タイトルなし"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知設定を開く"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 92368300422a..4fec28473a7f 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -879,8 +879,7 @@ <item quantity="one">თქვენი <xliff:g id="TYPE_1">%2$s</xliff:g> გამოიყენება <xliff:g id="NUM_APPS_0">%1$d</xliff:g> აპლიკაციის მიერ.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"გასაგებია"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"კონფიდ. პარამეტრები"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"აპი, რომლის მიერაც გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"აპები, რომელთა მიერაც გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის შეტყობინების პარამეტრების გახსნა"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 3b46364d8faf..c5022b55cacd 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 6811072ef0ff..c03dddd43ba6 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 273470ada54e..aa0e63f855be 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index ca45e4ddedc6..eb949037461e 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 82d709b52359..14d93e498e8f 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index ed0540780031..467a2dc77b67 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index a136772baad4..c79743ffdbd0 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -893,8 +893,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> programų naudoja jūsų <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Supratau"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privatumo nustatymai"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Programa, kuri naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Programos, kurios naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -911,8 +910,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Jutikliai išjungti"</string> <string name="device_services" msgid="1191212554435440592">"Įrenginio paslaugos"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Nėra pavadinimo"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Atidaryti „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Atidaryti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų nustatymus"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index fec4fe8ca672..d6dc8f34c6dc 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -886,8 +886,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> lietojumprogrammās tiek izmantots: <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Labi"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Konfidencialitāte"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Lietotne, kurā tiek izmantots: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Lietotnes, kurās tiek izmantots: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -903,8 +902,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensori izslēgti"</string> <string name="device_services" msgid="1191212554435440592">"Ierīces pakalpojumi"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Nav nosaukuma"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Atvērt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Atvērt paziņojumu iestatījumus lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 6b3e8cc111e5..03e31f745794 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 37d4b8c85f96..f25ae1d0e1d8 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 42cb765369b1..0e5dbb71d39a 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -879,8 +879,7 @@ <item quantity="one">Таны <xliff:g id="TYPE_1">%2$s</xliff:g>-г <xliff:g id="NUM_APPS_0">%1$d</xliff:g> апп ашиглаж байна.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Ойлголоо"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Нууцлалын тохиргоо"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Апп таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Аппууд таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн тохиргоог нээх"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 44d8c4e8f3ff..1b81062cc247 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index eee6b25bcddf..df10b2ffa438 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index b341bea7d19b..f8278059919d 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -684,7 +684,7 @@ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"ဂဏန်းကွက်<xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"စနစ်"</string> <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ပင်မ"</string> - <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"မကြာသေးခင်က"</string> + <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"လတ်တလော"</string> <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> @@ -879,8 +879,7 @@ <item quantity="one">အပလီကေးရှင်း <xliff:g id="NUM_APPS_0">%1$d</xliff:g> ခုက သင်၏ <xliff:g id="TYPE_1">%2$s</xliff:g> ကို အသုံးပြုနေသည်။</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"ရပါပြီ"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"ကန့်သတ်ဆက်တင်များ"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"သင့် <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသော အက်ပ်"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"သင့် <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသော အက်ပ်များ"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"၊ "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ဆက်တင်များကို ဖွင့်ရန်"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index a27addba20de..1370468c32e5 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 1bb366481b25..08f3cef84582 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 8075ef4cd6af..7cb4b5a359c0 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -792,7 +792,7 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g>-instellingen openen."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Volgorde van instellingen bewerken."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <string name="tuner_lock_screen" msgid="5755818559638850294">"Scherm vergrendelen"</string> + <string name="tuner_lock_screen" msgid="5755818559638850294">"Vergrendelingsscherm"</string> <string name="pip_phone_expand" msgid="5889780005575693909">"Uitvouwen"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimaliseren"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Sluiten"</string> @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> app gebruikt je <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Privacyinstellingen"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App die je <xliff:g id="TYPES_LIST">%s</xliff:g> gebruikt"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps die je <xliff:g id="TYPES_LIST">%s</xliff:g> gebruiken"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensoren uit"</string> <string name="device_services" msgid="1191212554435440592">"Apparaatservices"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Geen titel"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"<xliff:g id="APP_NAME">%1$s</xliff:g> openen"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Instellingen voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> openen"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 337b15d219f7..b9b4ea8f8360 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 4d8e3f11ceab..5432975a7320 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index d955236c3b09..b69204593082 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -893,8 +893,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplikacja używa: <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Ustawienia prywatności"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacje, które używają: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacje, które używają: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -911,8 +910,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Wyłącz czujniki"</string> <string name="device_services" msgid="1191212554435440592">"Usługi urządzenia"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Bez tytułu"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Otwórz: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Otwórz ustawienia powiadomień z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index d2219d259522..fcd7f85cf6f4 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> aplicativos estão usando <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Ok"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Config. de privacidade"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string> <string name="device_services" msgid="1191212554435440592">"Serviços do dispositivo"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Sem título"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Abrir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Abra as configurações de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 0866ffc7acea..6579645c83b9 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplicação está a utilizar o(a) <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Compreendi"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Def. de privacidade"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicações que utilizam o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicações que utilizam o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string> <string name="device_services" msgid="1191212554435440592">"Serviços do dispositivo"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Sem título"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Abrir a aplicação <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Abrir as definições de notificação da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index d2219d259522..fcd7f85cf6f4 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> aplicativos estão usando <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Ok"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Config. de privacidade"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string> <string name="device_services" msgid="1191212554435440592">"Serviços do dispositivo"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Sem título"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Abrir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Abra as configurações de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index e04048458ea6..144bfc38f677 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -886,8 +886,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplicație folosește <xliff:g id="TYPE_1">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Setări de confidențialitate"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicație care folosește <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicații care folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -903,8 +902,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori dezactivați"</string> <string name="device_services" msgid="1191212554435440592">"Servicii pentru dispozitiv"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Fără titlu"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Accesați <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Deschideți setările pentru notificări pentru aplicația <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 338ce14764a0..6d65592ad549 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -893,8 +893,7 @@ <item quantity="other">Функцию \"<xliff:g id="TYPE_5">%2$s</xliff:g>\" используют <xliff:g id="NUM_APPS_4">%1$d</xliff:g> приложения.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"ОК"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Конфиденциальность"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Приложение, в котором используются операции <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Приложения, в которых используются операции <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -911,8 +910,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"Настройки уведомлений приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 3a0b3a4200ba..2529f19370c0 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -879,8 +879,7 @@ <item quantity="other">යෙදුම් <xliff:g id="NUM_APPS_4">%1$d</xliff:g>ක් ඔබේ <xliff:g id="TYPE_5">%2$s</xliff:g> භාවිත කරමින් සිටිති.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"තේරුණා"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"පෞද්ගලිකත්ව සැකසීම්"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ඔබගේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරන යෙදුම්"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ඔබගේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරන යෙදුම්"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් සැකසීම් විවෘත කරන්න"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 883243e65496..923384f93511 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -893,8 +893,7 @@ <item quantity="one"><xliff:g id="TYPE_1">%2$s</xliff:g> používa <xliff:g id="NUM_APPS_0">%1$d</xliff:g> aplikácia.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Dobre"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Nastavenia ochrany súkromia"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikácia používajúca <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikácie používajúce <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -911,8 +910,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzory sú vypnuté"</string> <string name="device_services" msgid="1191212554435440592">"Služby zariadenia"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Bez názvu"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Otvoriť <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Otvoriť nastavenia upozornení pre <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index db40b44c4d17..d497f26926de 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -915,4 +915,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index ad6113e74c2b..db905cebb30d 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 768ae7fa2a2d..264698a779b7 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -886,8 +886,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> апликација користи дозволу <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Важи"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Подешав. приватности"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Апликација која користи дозволе <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Апликације које користе дозволе <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -903,8 +902,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"Отворите подешавања обавештења за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 0f36517ccce0..7e8446e1a427 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index a11e4d44e1c0..eb658ff17cfb 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index a8587c121815..5bcc6fec7d39 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index a512c0c0adc4..cb9b70d6bbdf 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index b8a840eedaad..6a126ff00684 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -879,8 +879,7 @@ <item quantity="one">มี <xliff:g id="NUM_APPS_0">%1$d</xliff:g> แอปพลิเคชันกำลังใช้<xliff:g id="TYPE_1">%2$s</xliff:g></item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"รับทราบ"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"การตั้งค่าความเป็นส่วนตัว"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"มีแอปกำลังใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณ"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"มีหลายแอปกำลังใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณ"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"เปิดการตั้งค่าการแจ้งเตือนสำหรับ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 72c5eacf806c..1550787b4b27 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -879,8 +879,7 @@ <item quantity="other">Ginagamit ng <xliff:g id="NUM_APPS_4">%1$d</xliff:g> na application ang iyong <xliff:g id="TYPE_5">%2$s</xliff:g>.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Mga setting ng privacy"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App na gumagamit ng iyong <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Mga app na gumagamit ng iyong <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Naka-off ang mga sensor"</string> <string name="device_services" msgid="1191212554435440592">"Mga Serbisyo ng Device"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Walang pamagat"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Buksan ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Buksan ang mga setting ng notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 5cd4e4c5f39f..93671558e3ef 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index b231c581b212..a850f213ca94 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -915,4 +915,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 90447d777794..c7bf3b0e4e0a 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> ایپلیکیشن آپ کی <xliff:g id="TYPE_1">%2$s</xliff:g> کا استعمال کر رہی ہے۔</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"سمجھ آ گئی"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"رازداری کی ترتیبات"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ایپ آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ایپس آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"، "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لئے اطلاع کی ترتیبات کھولیں"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 4a7af622bc64..48532fad0c1d 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> ta ilova <xliff:g id="TYPE_1">%2$s</xliff:g> ishlatmoqda.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Maxfiylik sozlama-ri"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> ishlatayotgan ilova"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Ilovalar <xliff:g id="TYPES_LIST">%s</xliff:g> ishlatmoqda"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorlar nofaol"</string> <string name="device_services" msgid="1191212554435440592">"Qurilma xizmatlari"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Nomsiz"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Ochish: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirishnoma sozlamalarini ochish"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index ad8d17cc23c1..b796503bbf3d 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -879,8 +879,7 @@ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> ứng dụng đang dùng <xliff:g id="TYPE_1">%2$s</xliff:g> của bạn.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"OK"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Cài đặt quyền riêng tư"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Ứng dụng đang sử dụng <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Các ứng dụng đang sử dụng <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Tắt cảm biến"</string> <string name="device_services" msgid="1191212554435440592">"Dịch vụ cho thiết bị"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Không có tiêu đề"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Mở <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"Mở mục cài đặt thông báo dành cho <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 0330edf023e0..3a5dd5cda55d 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -899,4 +899,10 @@ <skip /> <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> <skip /> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> + <skip /> + <!-- no translation found for yes_bubbles (668809525728633841) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 01797fb83fd2..8d67f5c7a5dc 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -879,8 +879,7 @@ <item quantity="one">有 <xliff:g id="NUM_APPS_0">%1$d</xliff:g> 個應用程式正在使用您的<xliff:g id="TYPE_1">%2$s</xliff:g>。</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"知道了"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"隱私權設定"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"開啟「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知設定"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 62b7cb667937..d8d0c07bcbc0 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -879,8 +879,7 @@ <item quantity="one">有 <xliff:g id="NUM_APPS_0">%1$d</xliff:g> 個應用程式正在使用你的<xliff:g id="TYPE_1">%2$s</xliff:g>。</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"我知道了"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"隱私權設定"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string> @@ -895,8 +894,12 @@ <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 bubbles_deep_link_button_description (8895837143057564517) --> + <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="1940331766151865776">"開啟「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知設定"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index f8bf2e39d748..2e0efcc863c7 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -879,8 +879,7 @@ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> izinhlelo zokusebenza zisebenzisa i-<xliff:g id="TYPE_5">%2$s</xliff:g> yakho.</item> </plurals> <string name="ongoing_privacy_dialog_ok" msgid="3273300106348958308">"Ngiyezwa"</string> - <!-- no translation found for ongoing_privacy_dialog_open_settings (6773015940472748876) --> - <skip /> + <string name="ongoing_privacy_dialog_open_settings" msgid="6773015940472748876">"Izilungiselelo zobumfihlo"</string> <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Uhlelo lokusebenza olusebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho"</string> <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Izinhlelo zokusebenza ezisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho"</string> <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string> @@ -895,8 +894,12 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Izinzwa zivaliwe"</string> <string name="device_services" msgid="1191212554435440592">"Amasevisi edivayisi"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Asikho isihloko"</string> - <!-- no translation found for bubbles_deep_link_button_description (8895837143057564517) --> + <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"Vula i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1940331766151865776">"VUla izilungiselelo zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for bubbles_prompt (2684301469286150276) --> + <skip /> + <!-- no translation found for no_bubbles (7173621233904687258) --> <skip /> - <!-- no translation found for bubbles_settings_button_description (1940331766151865776) --> + <!-- no translation found for yes_bubbles (668809525728633841) --> <skip /> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 03445352e330..1e1245fe0d86 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -967,7 +967,7 @@ <!-- Height and width of App Opp icons in Ongoing App Ops dialog --> <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen> <!-- Left margin of App Opp icons in Ongoing App Ops dialog --> - <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen> + <dimen name="ongoing_appops_dialog_icon_margin">12dp</dimen> <!-- Height and width of Application icons in Ongoing App Ops dialog --> <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen> <!-- Height and width of Plus sign in Ongoing App Ops dialog --> @@ -988,12 +988,14 @@ <dimen name="ongoing_appops_chip_side_padding">6dp</dimen> <!-- Padding between background of Ongoing App Ops chip and content --> <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen> - <!-- Margin between icons of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen> + <!-- Margin between icons of Ongoing App Ops chip when QQS--> + <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen> + <!-- Margin between icons of Ongoing App Ops chip when QS--> + <dimen name="ongoing_appops_chip_icon_margin_expanded">8dp</dimen> <!-- Icon size of Ongoing App Ops chip --> <dimen name="ongoing_appops_chip_icon_size">18dp</dimen> <!-- Radius of Ongoing App Ops chip corners --> - <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen> + <dimen name="ongoing_appops_chip_bg_corner_radius">4dp</dimen> <!-- Text size for Ongoing App Ops dialog title --> <dimen name="ongoing_appops_dialog_title_size">20sp</dimen> <!-- Text size for Ongoing App Ops dialog items --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index db4a6cc1d704..89c6c8a66231 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2301,6 +2301,9 @@ <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]--> <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string> + <!-- Ongoing Privacy "Chip" in use text [CHAR LIMIT=10]--> + <string name="ongoing_privacy_chip_in_use">In use:</string> + <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]--> <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op"> <item quantity="one"><xliff:g id="num_apps" example="1">%1$d</xliff:g> application is using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java new file mode 100644 index 000000000000..f7ccb816b675 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import android.os.Looper; +import android.util.Pair; +import android.view.BatchedInputEventReceiver; +import android.view.Choreographer; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.InputEventSender; + +/** + * @see android.view.InputChannel + */ +public class InputChannelCompat { + + /** + * Callback for receiving event callbacks + */ + public interface InputEventListener { + /** + * @param ev event to be handled + */ + void onInputEvent(InputEvent ev); + } + + /** + * Creates a dispatcher and receiver pair to better handle events across threads. + */ + public static Pair<InputEventDispatcher, InputEventReceiver> createPair(String name, + Looper looper, Choreographer choreographer, InputEventListener listener) { + InputChannel[] channels = InputChannel.openInputChannelPair(name); + + InputEventDispatcher dispatcher = new InputEventDispatcher(channels[0], looper); + InputEventReceiver receiver = new InputEventReceiver(channels[1], looper, choreographer, + listener); + return Pair.create(dispatcher, receiver); + } + + /** + * @see BatchedInputEventReceiver + */ + public static class InputEventReceiver { + + private final BatchedInputEventReceiver mReceiver; + private final InputChannel mInputChannel; + + public InputEventReceiver(InputChannel inputChannel, Looper looper, + Choreographer choreographer, final InputEventListener listener) { + mInputChannel = inputChannel; + mReceiver = new BatchedInputEventReceiver(inputChannel, looper, choreographer) { + + @Override + public void onInputEvent(InputEvent event) { + listener.onInputEvent(event); + finishInputEvent(event, true /* handled */); + } + }; + } + + /** + * @see BatchedInputEventReceiver#dispose() + */ + public void dispose() { + mReceiver.dispose(); + mInputChannel.dispose(); + } + } + + /** + * @see InputEventSender + */ + public static class InputEventDispatcher { + + private final InputChannel mInputChannel; + private final InputEventSender mSender; + + private InputEventDispatcher(InputChannel inputChannel, Looper looper) { + mInputChannel = inputChannel; + mSender = new InputEventSender(inputChannel, looper) { }; + } + + /** + * @see InputEventSender#sendInputEvent(int, InputEvent) + */ + public void dispatch(InputEvent ev) { + mSender.sendInputEvent(ev.getSequenceNumber(), ev); + } + + /** + * @see InputEventSender#dispose() + */ + public void dispose() { + mSender.dispose(); + mInputChannel.dispose(); + } + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index 2bdbf0bf3c22..221782e950d0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -20,6 +20,7 @@ import android.app.WindowConfiguration; import android.graphics.Point; import android.graphics.Rect; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; /** * @see RemoteAnimationTarget @@ -47,6 +48,8 @@ public class RemoteAnimationTargetCompat { public final boolean isNotInRecents; public final Rect contentInsets; + private final SurfaceControl mStartLeash; + public RemoteAnimationTargetCompat(RemoteAnimationTarget app) { taskId = app.taskId; mode = app.mode; @@ -59,6 +62,8 @@ public class RemoteAnimationTargetCompat { isNotInRecents = app.isNotInRecents; contentInsets = app.contentInsets; activityType = app.windowConfiguration.getActivityType(); + + mStartLeash = app.startLeash; } public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) { @@ -69,4 +74,14 @@ public class RemoteAnimationTargetCompat { } return appsCompat; } + + /** + * @see SurfaceControl#release() + */ + public void release() { + leash.mSurfaceControl.release(); + if (mStartLeash != null) { + mStartLeash.release(); + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index d5bd2b202803..1539582ef18b 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -299,8 +299,9 @@ public class CarrierTextController { displayText.toString().split(mSeparator.toString()), anySimReadyAndInService && !missingSimsWithSubs, subsIds); - if (mCarrierTextCallback != null) { - handler.post(() -> mCarrierTextCallback.updateCarrierInfo(info)); + final CarrierTextCallback callback = mCarrierTextCallback; + if (callback != null) { + handler.post(() -> callback.updateCarrierInfo(info)); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 7218acf614d4..fc1843ba982a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,5 +1,6 @@ package com.android.keyguard; +import android.app.WallpaperManager; import android.content.Context; import android.graphics.Paint; import android.graphics.Paint.Style; @@ -12,11 +13,13 @@ import android.widget.TextClock; import androidx.annotation.VisibleForTesting; +import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; import com.android.systemui.Dependency; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import java.util.TimeZone; @@ -50,6 +53,8 @@ public class KeyguardClockSwitch extends RelativeLayout { * Maintain state so that a newly connected plugin can be initialized. */ private float mDarkAmount; + private boolean mSupportsDarkText; + private int[] mColorPalette; private final StatusBarStateController.StateListener mStateListener = new StatusBarStateController.StateListener() { @@ -72,6 +77,21 @@ public class KeyguardClockSwitch extends RelativeLayout { private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; + /** + * Listener for changes to the color palette. + * + * The color palette changes when the wallpaper is changed. + */ + private SysuiColorExtractor.OnColorsChangedListener mColorsListener = (extractor, which) -> { + if ((which & WallpaperManager.FLAG_LOCK) != 0) { + if (extractor instanceof SysuiColorExtractor) { + updateColors((SysuiColorExtractor) extractor); + } else { + updateColors(Dependency.get(SysuiColorExtractor.class)); + } + } + }; + public KeyguardClockSwitch(Context context) { this(context, null); } @@ -100,6 +120,9 @@ public class KeyguardClockSwitch extends RelativeLayout { super.onAttachedToWindow(); Dependency.get(ClockManager.class).addOnClockChangedListener(mClockChangedListener); Dependency.get(StatusBarStateController.class).addCallback(mStateListener); + SysuiColorExtractor colorExtractor = Dependency.get(SysuiColorExtractor.class); + colorExtractor.addOnColorsChangedListener(mColorsListener); + updateColors(colorExtractor); } @Override @@ -107,6 +130,8 @@ public class KeyguardClockSwitch extends RelativeLayout { super.onDetachedFromWindow(); Dependency.get(ClockManager.class).removeOnClockChangedListener(mClockChangedListener); Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); + Dependency.get(SysuiColorExtractor.class) + .removeOnColorsChangedListener(mColorsListener); } private void setClockPlugin(ClockPlugin plugin) { @@ -149,6 +174,9 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockPlugin.setStyle(getPaint().getStyle()); mClockPlugin.setTextColor(getCurrentTextColor()); mClockPlugin.setDarkAmount(mDarkAmount); + if (mColorPalette != null) { + mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette); + } } /** @@ -246,6 +274,16 @@ public class KeyguardClockSwitch extends RelativeLayout { } } + private void updateColors(SysuiColorExtractor colorExtractor) { + ColorExtractor.GradientColors colors = colorExtractor.getColors(WallpaperManager.FLAG_LOCK, + true); + mSupportsDarkText = colors.supportsDarkText(); + mColorPalette = colors.getColorPalette(); + if (mClockPlugin != null) { + mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette); + } + } + @VisibleForTesting (otherwise = VisibleForTesting.NONE) ClockManager.ClockChangedListener getClockChangedListener() { return mClockChangedListener; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java index db6127f1d573..3114708de038 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -89,7 +89,17 @@ public class BubbleClockController implements ClockPlugin { @Override public void setTextColor(int color) { mLockClock.setTextColor(color); - mDigitalClock.setTextColor(color); + } + + @Override + public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { + if (colorPalette == null || colorPalette.length == 0) { + return; + } + final int length = colorPalette.length; + mDigitalClock.setTextColor(colorPalette[Math.max(0, length - 6)]); + mAnalogClock.setClockColors(colorPalette[Math.max(0, length - 6)], + colorPalette[Math.max(0, length - 3)]); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java index 2c709e0393bd..e35cf113c111 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java @@ -78,6 +78,16 @@ public class ImageClock extends FrameLayout { mTime.setTimeZone(timeZone); } + /** + * Sets the colors to use on the clock face. + * @param dark Darker color obtained from color palette. + * @param light Lighter color obtained from color palette. + */ + public void setClockColors(int dark, int light) { + mHourHand.setColorFilter(dark); + mMinuteHand.setColorFilter(light); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java index 8734754541a6..3c9a4f821c62 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java @@ -76,10 +76,12 @@ public class StretchAnalogClock extends View { } /** - * Set the color of the minute hand. + * Set the colors to use on the clock face. + * @param dark Darker color obtained from color palette. + * @param light Lighter color obtained from color palette. */ - public void setMinuteHandColor(int color) { - mMinutePaint.setColor(color); + public void setClockColor(int dark, int light) { + mHourPaint.setColor(dark); invalidate(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java index 0a39158cd4be..c4651149521c 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java @@ -89,8 +89,17 @@ public class StretchAnalogClockController implements ClockPlugin { @Override public void setTextColor(int color) { mLockClock.setTextColor(color); - mDigitalClock.setTextColor(color); - mAnalogClock.setMinuteHandColor(color); + } + + @Override + public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { + if (colorPalette == null || colorPalette.length == 0) { + return; + } + final int length = colorPalette.length; + mDigitalClock.setTextColor(colorPalette[Math.max(0, length - 5)]); + mAnalogClock.setClockColor(colorPalette[Math.max(0, length - 5)], + colorPalette[Math.max(0, length - 2)]); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java index 17d929dc8a3b..2ea39c40bee2 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java @@ -87,6 +87,15 @@ public class TypeClockController implements ClockPlugin { } @Override + public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { + if (colorPalette == null || colorPalette.length == 0) { + return; + } + final int length = colorPalette.length; + mTypeClock.setClockColor(colorPalette[Math.max(0, length - 5)]); + } + + @Override public void dozeTimeTick() { mTypeClock.onTimeChanged(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java index 8feae53159ac..6f1b59c69865 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java @@ -43,7 +43,7 @@ public class TypographicClock extends TextView { private final Resources mResources; private final String[] mHours; private final String[] mMinutes; - private final int mAccentColor; + private int mAccentColor; private Calendar mTime; private String mDescFormat; private TimeZone mTimeZone; @@ -106,6 +106,13 @@ public class TypographicClock extends TextView { mTime.setTimeZone(timeZone); } + /** + * Sets the accent color used on the clock face. + */ + public void setClockColor(int color) { + mAccentColor = color; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index fece94e69a3d..567207376996 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -18,6 +18,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.hardware.SensorPrivacyManager; +import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.os.Looper; import android.util.ArrayMap; @@ -25,7 +26,6 @@ import android.util.DisplayMetrics; import android.view.IWindowManager; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.ColorDisplayController; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.Preconditions; @@ -42,6 +42,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.PowerUI; import com.android.systemui.privacy.PrivacyItemController; @@ -55,7 +56,6 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SmartReplyController; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -206,7 +206,7 @@ public class Dependency extends SystemUI { @Inject Lazy<UserInfoController> mUserInfoController; @Inject Lazy<KeyguardMonitor> mKeyguardMonitor; @Inject Lazy<BatteryController> mBatteryController; - @Inject Lazy<ColorDisplayController> mColorDisplayController; + @Inject Lazy<NightDisplayListener> mNightDisplayListener; @Inject Lazy<ManagedProfileController> mManagedProfileController; @Inject Lazy<NextAlarmController> mNextAlarmController; @Inject Lazy<DataSaverController> mDataSaverController; @@ -330,7 +330,7 @@ public class Dependency extends SystemUI { mProviders.put(BatteryController.class, mBatteryController::get); - mProviders.put(ColorDisplayController.class, mColorDisplayController::get); + mProviders.put(NightDisplayListener.class, mNightDisplayListener::get); mProviders.put(ManagedProfileController.class, mManagedProfileController::get); diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java index 88e32cbeaa29..a517d7ce8e0e 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java @@ -24,6 +24,7 @@ import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.annotation.Nullable; import android.content.Context; import android.hardware.SensorPrivacyManager; +import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -34,7 +35,6 @@ import android.util.DisplayMetrics; import android.view.IWindowManager; import android.view.WindowManagerGlobal; -import com.android.internal.app.ColorDisplayController; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -152,8 +152,8 @@ public class DependencyProvider { @Singleton @Provides - public ColorDisplayController provideColorDisplayController(Context context) { - return new ColorDisplayController(context); + public NightDisplayListener provideNightDisplayListener(Context context) { + return new NightDisplayListener(context); } @Singleton diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 50409421a68b..2aecc24e83c0 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -19,11 +19,8 @@ package com.android.systemui; import static android.view.Display.DEFAULT_DISPLAY; import android.app.WallpaperManager; -import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.RecordingCanvas; @@ -31,9 +28,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region.Op; import android.hardware.display.DisplayManager; -import android.opengl.GLSurfaceView; import android.os.AsyncTask; -import android.os.Build; import android.os.Handler; import android.os.Trace; import android.service.wallpaper.WallpaperService; @@ -44,7 +39,6 @@ import android.view.Surface; import android.view.SurfaceHolder; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.glwallpaper.ImageWallpaperRenderer; import java.io.FileDescriptor; import java.io.IOException; @@ -57,17 +51,12 @@ import java.io.PrintWriter; public class ImageWallpaper extends WallpaperService { private static final String TAG = "ImageWallpaper"; private static final String GL_LOG_TAG = "ImageWallpaperGL"; - // TODO: Testing purpose, need to remove later, b/123616712. - private static final String SENSOR_EVENT_AWAKE = "systemui.test.event.awake"; - // TODO: Testing purpose, need to remove later, b/123616712. - private static final String SENSOR_EVENT_SLEEP = "systemui.test.event.sleep"; private static final boolean DEBUG = false; private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu"; private static final long DELAY_FORGET_WALLPAPER = 5000; private WallpaperManager mWallpaperManager; private DrawableEngine mEngine; - private GLEngine mGlEngine; @Override public void onCreate() { @@ -84,112 +73,10 @@ public class ImageWallpaper extends WallpaperService { @Override public Engine onCreateEngine() { - mGlEngine = new GLEngine(this); - return mGlEngine; + mEngine = new DrawableEngine(); + return mEngine; } - class GLEngine extends Engine { - private GLWallpaperSurfaceView mWallpaperSurfaceView; - - GLEngine(Context context) { - mWallpaperSurfaceView = new GLWallpaperSurfaceView(context); - mWallpaperSurfaceView.setRenderer( - new ImageWallpaperRenderer(context, mWallpaperSurfaceView)); - mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - setOffsetNotificationsEnabled(true); - } - - @Override - public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { - if (mWallpaperSurfaceView != null) { - mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode); - } - } - - @Override - public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, - float yOffsetStep, int xPixelOffset, int yPixelOffset) { - if (mWallpaperSurfaceView != null) { - mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset); - } - } - - private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView { - private SensorEventListener mEventListener; - private WallpaperStatusListener mWallpaperChangedListener; - - // TODO: Testing purpose, need to remove later, b/123616712. - /** - * For testing only: adb shell am broadcast -a <INTENT> - */ - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent == null) { - return; - } - switch (intent.getAction()) { - case SENSOR_EVENT_AWAKE: - notifySensorEvents(true); - break; - case SENSOR_EVENT_SLEEP: - notifySensorEvents(false); - break; - } - } - }; - - GLWallpaperSurfaceView(Context context) { - super(context); - setEGLContextClientVersion(2); - // TODO: Testing purpose, need to remove later, b/123616712. - if (Build.IS_DEBUGGABLE) { - IntentFilter filter = new IntentFilter(); - filter.addAction(SENSOR_EVENT_AWAKE); - filter.addAction(SENSOR_EVENT_SLEEP); - registerReceiver(mReceiver, filter); - } - } - - @Override - public SurfaceHolder getHolder() { - return getSurfaceHolder(); - } - - @Override - public void setRenderer(Renderer renderer) { - super.setRenderer(renderer); - mEventListener = (SensorEventListener) renderer; - mWallpaperChangedListener = (WallpaperStatusListener) renderer; - } - - private void notifySensorEvents(boolean reach) { - if (mEventListener != null) { - mEventListener.onSensorEvent(reach); - } - } - - private void notifyAmbientModeChanged(boolean inAmbient) { - if (mWallpaperChangedListener != null) { - mWallpaperChangedListener.onAmbientModeChanged(inAmbient); - } - } - - private void notifyOffsetsChanged(float xOffset, float yOffset) { - if (mWallpaperChangedListener != null) { - mWallpaperChangedListener.onOffsetsChanged( - xOffset, yOffset, getHolder().getSurfaceFrame()); - } - } - - @Override - public void render() { - requestRender(); - } - } - } - - // TODO: Remove this engine, tracking on b/123617158. class DrawableEngine extends Engine { private final Runnable mUnloadWallpaperCallback = () -> { unloadWallpaper(false /* forgetSize */); @@ -677,46 +564,4 @@ public class ImageWallpaper extends WallpaperService { } } } - - /** - * A listener to trace sensor event. - */ - public interface SensorEventListener { - - /** - * Called back while sensor event comes. - * @param reach The status of sensor. - */ - void onSensorEvent(boolean reach); - } - - /** - * A listener to trace status of image wallpaper. - */ - public interface WallpaperStatusListener { - - /** - * Called back while ambient mode changes. - * @param inAmbientMode true if is in ambient mode, false otherwise. - */ - void onAmbientModeChanged(boolean inAmbientMode); - - /** - * Called back while wallpaper offsets. - * @param xOffset The offset portion along x. - * @param yOffset The offset portion along y. - */ - void onOffsetsChanged(float xOffset, float yOffset, Rect frame); - } - - /** - * An abstraction for view of GLRenderer. - */ - public interface ImageGLView { - - /** - * Ask the view to render. - */ - void render(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 9b3d7ed8f045..755d6fcffb43 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -33,6 +33,7 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.EnhancedEstimatesImpl; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -40,7 +41,7 @@ import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationData; @@ -152,6 +153,15 @@ public class SystemUIFactory { return new VolumeDialogComponent(systemUi, context); } + /** + * Provides status bar state controller implementation + */ + @Singleton + @Provides + public StatusBarStateController provideStatusBarStateController(Context context) { + return new StatusBarStateControllerImpl(); + } + @Singleton @Provides public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index f36dca7fbade..41bc1b29075c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -16,6 +16,10 @@ package com.android.systemui.bubbles; +import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; +import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; +import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE; +import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -24,9 +28,11 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.notification.NotificationAlertingManager.alertAgain; import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; import android.app.INotificationManager; import android.app.Notification; -import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ActivityInfo; @@ -37,6 +43,8 @@ import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.util.StatsLog; +import android.view.Display; import android.view.LayoutInflater; import android.view.ViewGroup; import android.view.WindowManager; @@ -48,7 +56,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; @@ -57,6 +67,7 @@ import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.phone.StatusBarWindowController; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -70,7 +81,7 @@ import javax.inject.Singleton; * The controller manages addition, removal, and visible state of bubbles on screen. */ @Singleton -public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlockedListener { +public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener { private static final int MAX_BUBBLES = 5; // TODO: actually enforce this private static final String TAG = "BubbleController"; @@ -90,6 +101,8 @@ public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlo private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; + private final IActivityTaskManager mActivityTaskManager; + private final BubbleTaskStackListener mTaskStackListener; private BubbleStateChangeListener mStateChangeListener; private BubbleExpandListener mExpandListener; private LayoutInflater mInflater; @@ -173,6 +186,10 @@ public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlo mStatusBarWindowController = statusBarWindowController; mStatusBarStateListener = new StatusBarStateListener(); Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener); + + mActivityTaskManager = ActivityTaskManager.getService(); + mTaskStackListener = new BubbleTaskStackListener(); + ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); } /** @@ -427,11 +444,15 @@ public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlo @Nullable private PendingIntent getValidBubbleIntent(NotificationEntry notif) { Notification notification = notif.notification.getNotification(); + String packageName = notif.notification.getPackageName(); Notification.BubbleMetadata data = notif.getBubbleMetadata(); - if (data != null && canLaunchInActivityView(data.getIntent())) { + if (data != null && canLaunchInActivityView(data.getIntent(), + true /* enable logging for bubbles */, packageName)) { return data.getIntent(); - } else if (shouldUseContentIntent(mContext) - && canLaunchInActivityView(notification.contentIntent)) { + } + if (shouldUseContentIntent(mContext) + && canLaunchInActivityView(notification.contentIntent, + false /* disable logging for notifications */, packageName)) { Log.d(TAG, "[addBubble " + notif.key + "]: No appOverlayIntent, using contentIntent."); return notification.contentIntent; @@ -442,16 +463,41 @@ public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlo /** * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. + * + * @param intent the pending intent of the bubble. + * @param enableLogging whether bubble developer error should be logged. + * @param packageName the notification package name for this bubble. + * @return */ - private boolean canLaunchInActivityView(PendingIntent intent) { + private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging, + String packageName) { if (intent == null) { return false; } ActivityInfo info = intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0); - return info != null - && ActivityInfo.isResizeableMode(info.resizeMode) - && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0; + if (info == null) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); + } + return false; + } + if (!ActivityInfo.isResizeableMode(info.resizeMode)) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); + } + return false; + } + if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS); + } + return false; + } + return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0; } /** @@ -460,20 +506,9 @@ public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlo @VisibleForTesting protected boolean shouldBubble(NotificationEntry entry) { StatusBarNotification n = entry.notification; - boolean canAppOverlay = false; - try { - canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage( - n.getPackageName(), n.getUid()); - } catch (RemoteException e) { - Log.w(TAG, "Error calling NoMan to determine if app can overlay", e); - } - - NotificationChannel channel = mNotificationEntryManager.getNotificationData().getChannel( - entry.key); - boolean canChannelOverlay = channel != null && channel.canBubble(); boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null && n.getNotification().getBubbleMetadata().getIntent() != null; - return hasOverlayIntent && canChannelOverlay && canAppOverlay; + return hasOverlayIntent && entry.canBubble; } /** @@ -512,6 +547,64 @@ public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlo || autoBubbleAll; } + /** + * This task stack listener is responsible for responding to tasks moved to the front + * which are on the default (main) display. When this happens, expanded bubbles must be + * collapsed so the user may interact with the app which was just moved to the front. + * <p> + * This listener is registered with SystemUI's ActivityManagerWrapper which dispatches + * these calls via a main thread Handler. + */ + @MainThread + private class BubbleTaskStackListener extends TaskStackChangeListener { + + @Nullable + private ActivityManager.StackInfo findStackInfo(int taskId) throws RemoteException { + final List<ActivityManager.StackInfo> stackInfoList = + mActivityTaskManager.getAllStackInfos(); + // Iterate through stacks from top to bottom. + final int stackCount = stackInfoList.size(); + for (int stackIndex = 0; stackIndex < stackCount; stackIndex++) { + final ActivityManager.StackInfo stackInfo = stackInfoList.get(stackIndex); + // Iterate through tasks from top to bottom. + for (int taskIndex = stackInfo.taskIds.length - 1; taskIndex >= 0; taskIndex--) { + if (stackInfo.taskIds[taskIndex] == taskId) { + return stackInfo; + } + } + } + return null; + } + + @Override + public void onTaskMovedToFront(int taskId) { + ActivityManager.StackInfo stackInfo = null; + try { + stackInfo = findStackInfo(taskId); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + if (stackInfo != null && stackInfo.displayId == Display.DEFAULT_DISPLAY + && mStackView != null) { + mStackView.collapseStack(); + } + } + + /** + * This is a workaround for the case when the activity had to be created in a new task. + * Existing code in ActivityStackSupervisor checks the display where the activity + * ultimately ended up, displays an error message toast, and calls this method instead of + * onTaskMovedToFront. + */ + // TODO(b/124058588): add requestedDisplayId to this callback, ignore unless matches + @Override + public void onActivityLaunchOnSecondaryDisplayFailed() { + if (mStackView != null) { + mStackView.collapseStack(); + } + } + } + private static boolean shouldAutoBubbleMessages(Context context) { return Settings.Secure.getInt(context.getContentResolver(), ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index f08ba1936b40..976a766dcc09 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -34,8 +34,10 @@ import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.util.AttributeSet; import android.util.Log; +import android.util.StatsLog; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageButton; @@ -51,7 +53,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** * Container for the expanded bubble view, handles rendering the caret and header of the view. */ -public class BubbleExpandedViewContainer extends LinearLayout implements View.OnClickListener { +public class BubbleExpandedView extends LinearLayout implements View.OnClickListener { private static final String TAG = "BubbleExpandedView"; // The triangle pointing to the expanded view @@ -81,19 +83,19 @@ public class BubbleExpandedViewContainer extends LinearLayout implements View.On private OnBubbleBlockedListener mOnBubbleBlockedListener; - public BubbleExpandedViewContainer(Context context) { + public BubbleExpandedView(Context context) { this(context, null); } - public BubbleExpandedViewContainer(Context context, AttributeSet attrs) { + public BubbleExpandedView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr) { + public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr, + public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mPm = context.getPackageManager(); @@ -234,6 +236,8 @@ public class BubbleExpandedViewContainer extends LinearLayout implements View.On mStackView.collapseStack(() -> { try { n.contentIntent.send(); + logBubbleClickEvent(mEntry.notification, + StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_APP); } catch (PendingIntent.CanceledException e) { Log.w(TAG, "Failed to send intent for bubble with key: " + (mEntry != null ? mEntry.key : " null entry")); @@ -242,7 +246,11 @@ public class BubbleExpandedViewContainer extends LinearLayout implements View.On } else if (id == R.id.settings_button) { Intent intent = getSettingsIntent(mEntry.notification.getPackageName(), mEntry.notification.getUid()); - mStackView.collapseStack(() -> mContext.startActivity(intent)); + mStackView.collapseStack(() -> { + mContext.startActivity(intent); + logBubbleClickEvent(mEntry.notification, + StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS); + }); } else if (id == R.id.no_bubbles_button) { setBubblesAllowed(false); } else if (id == R.id.yes_bubbles_button) { @@ -262,6 +270,9 @@ public class BubbleExpandedViewContainer extends LinearLayout implements View.On } else if (mOnBubbleBlockedListener != null) { mOnBubbleBlockedListener.onBubbleBlocked(mEntry); } + logBubbleClickEvent(mEntry.notification, + allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN : + StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT); } catch (RemoteException e) { Log.w(TAG, e); } @@ -318,4 +329,22 @@ public class BubbleExpandedViewContainer extends LinearLayout implements View.On */ void onBubbleBlocked(NotificationEntry entry); } + + /** + * Logs bubble UI click event. + * + * @param notification the bubble notification that user is interacting with. + * @param action the user interaction enum. + */ + private void logBubbleClickEvent(StatusBarNotification notification, int action) { + StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, + notification.getPackageName(), + notification.getNotification().getChannelId(), + notification.getId(), + mStackView.getBubbleIndex(mStackView.getExpandedBubble()), + mStackView.getBubbleCount(), + action, + mStackView.getNormalizedXPosition(), + mStackView.getNormalizedYPosition()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index afa9f02c6ee2..2b344f6cf195 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -41,6 +41,7 @@ import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; +import androidx.annotation.MainThread; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; @@ -94,7 +95,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private StackAnimationController mStackAnimationController; private ExpandedAnimationController mExpandedAnimationController; - private BubbleExpandedViewContainer mExpandedViewContainer; + private BubbleExpandedView mExpandedViewContainer; private int mBubbleSize; private int mBubblePadding; @@ -173,7 +174,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - mExpandedViewContainer = (BubbleExpandedViewContainer) + mExpandedViewContainer = (BubbleExpandedView) LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view, this /* parent */, false /* attachToRoot */); mExpandedViewContainer.setElevation(elevation); @@ -226,7 +227,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Sets the listener to notify when a bubble is blocked. */ - public void setOnBlockedListener(BubbleExpandedViewContainer.OnBubbleBlockedListener listener) { + public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) { mExpandedViewContainer.setOnBlockedListener(listener); } @@ -384,7 +385,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Collapses the stack of bubbles. + * <p> + * Must be called from the main thread. */ + @MainThread public void collapseStack() { if (mIsExpanded) { // TODO: Save opened bubble & move it to top of stack @@ -402,7 +406,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Expands the stack fo bubbles. + * <p> + * Must be called from the main thread. */ + @MainThread public void expandStack() { if (!mIsExpanded) { mExpandedBubble = getTopBubble(); @@ -766,7 +773,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * @return the number of bubbles in the stack view. */ - private int getBubbleCount() { + public int getBubbleCount() { return mBubbleContainer.getChildCount(); } @@ -777,14 +784,14 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the index of the bubble view within the bubble stack. The range of the position * is between 0 and the bubble count minus 1. */ - private int getBubbleIndex(BubbleView bubbleView) { + public int getBubbleIndex(BubbleView bubbleView) { return mBubbleContainer.indexOfChild(bubbleView); } /** * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ - private float getNormalizedXPosition() { + public float getNormalizedXPosition() { return new BigDecimal(getPosition().x / mDisplaySize.x) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); @@ -793,7 +800,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ - private float getNormalizedYPosition() { + public float getNormalizedYPosition() { return new BigDecimal(getPosition().y / mDisplaySize.y) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 2c23c0ccf614..74ddc8f6b8fc 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -22,6 +22,8 @@ import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -32,6 +34,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; @@ -266,6 +269,24 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati } } }); + mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { + ActivityView activityView = (ActivityView) view; + // Here we assume that the position of the ActivityView on the screen + // remains regardless of IME status. When we move ActivityView, the + // forwardedInsets should be computed not against the current location + // and size, but against the post-moved location and size. + Point displaySize = new Point(); + view.getContext().getDisplay().getSize(displaySize); + int[] windowLocation = view.getLocationOnScreen(); + final int windowBottom = windowLocation[1] + view.getHeight(); + final int keyboardHeight = insets.getSystemWindowInsetBottom() + - insets.getStableInsetBottom(); + final int insetsBottom = Math.max(0, + windowBottom + keyboardHeight - displaySize.y); + activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); + return view.onApplyWindowInsets(insets); + }); + } return mActivityView; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index 4ff27b1b64f0..8f215ff47855 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -35,9 +35,9 @@ import android.view.accessibility.AccessibilityManager; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; import com.android.systemui.analytics.DataCollector; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.util.AsyncSensorManager; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index fdf18ce24f50..900ea72d856c 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -48,6 +48,7 @@ import javax.inject.Singleton; @Singleton public class SysuiColorExtractor extends ColorExtractor implements Dumpable { private static final String TAG = "SysuiColorExtractor"; + private final Tonal mTonal; private boolean mWallpaperVisible; private boolean mHasBackdrop; // Colors to return when the wallpaper isn't visible @@ -61,6 +62,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable { @VisibleForTesting public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) { super(context, type); + mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context); mWpHiddenColors = new GradientColors(); WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM); @@ -94,7 +96,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable { } private void updateDefaultGradients(WallpaperColors colors) { - Tonal.applyFallback(colors, mWpHiddenColors); + mTonal.applyFallback(colors, mWpHiddenColors); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 4557b4de4901..d06feedfd9aa 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -110,7 +110,8 @@ public class DozeService extends DreamService @Override public void requestWakeUp() { PowerManager pm = getSystemService(PowerManager.class); - pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE"); + pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:NODOZE"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java deleted file mode 100644 index d03b00bcfc85..000000000000 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java +++ /dev/null @@ -1,115 +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.systemui.glwallpaper; - -import static android.opengl.GLES20.GL_FRAGMENT_SHADER; -import static android.opengl.GLES20.GL_VERTEX_SHADER; -import static android.opengl.GLES20.glAttachShader; -import static android.opengl.GLES20.glCompileShader; -import static android.opengl.GLES20.glCreateProgram; -import static android.opengl.GLES20.glCreateShader; -import static android.opengl.GLES20.glGetAttribLocation; -import static android.opengl.GLES20.glGetUniformLocation; -import static android.opengl.GLES20.glLinkProgram; -import static android.opengl.GLES20.glShaderSource; -import static android.opengl.GLES20.glUseProgram; - -import android.content.Context; -import android.content.res.Resources; -import android.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -/** - * This class takes charge of linking shader codes and then return a handle for OpenGL ES program. - */ -class ImageGLProgram { - private static final String TAG = ImageGLProgram.class.getSimpleName(); - - private Context mContext; - private int mProgramHandle; - - ImageGLProgram(Context context) { - mContext = context.getApplicationContext(); - } - - private int loadShaderProgram(int vertexId, int fragmentId) { - final String vertexSrc = getShaderResource(vertexId); - final String fragmentSrc = getShaderResource(fragmentId); - final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc); - final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc); - return getProgramHandle(vertexHandle, fragmentHandle); - } - - private String getShaderResource(int shaderId) { - Resources res = mContext.getResources(); - StringBuilder code = new StringBuilder(); - - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(res.openRawResource(shaderId)))) { - String nextLine; - while ((nextLine = reader.readLine()) != null) { - code.append(nextLine).append("\n"); - } - } catch (IOException | Resources.NotFoundException ex) { - Log.d(TAG, "Can not read the shader source", ex); - code = null; - } - - return code == null ? "" : code.toString(); - } - - private int getShaderHandle(int type, String src) { - final int shader = glCreateShader(type); - if (shader == 0) { - Log.d(TAG, "Create shader failed, type=" + type); - return 0; - } - glShaderSource(shader, src); - glCompileShader(shader); - return shader; - } - - private int getProgramHandle(int vertexHandle, int fragmentHandle) { - final int program = glCreateProgram(); - if (program == 0) { - Log.d(TAG, "Can not create OpenGL ES program"); - return 0; - } - - glAttachShader(program, vertexHandle); - glAttachShader(program, fragmentHandle); - glLinkProgram(program); - return program; - } - - boolean useGLProgram(int vertexResId, int fragmentResId) { - mProgramHandle = loadShaderProgram(vertexResId, fragmentResId); - glUseProgram(mProgramHandle); - return true; - } - - int getAttributeHandle(String name) { - return glGetAttribLocation(mProgramHandle, name); - } - - int getUniformHandle(String name) { - return glGetUniformLocation(mProgramHandle, name); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java deleted file mode 100644 index 4e07872c9456..000000000000 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java +++ /dev/null @@ -1,245 +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.systemui.glwallpaper; - -import static android.opengl.GLES20.GL_FLOAT; -import static android.opengl.GLES20.GL_LINEAR; -import static android.opengl.GLES20.GL_TEXTURE0; -import static android.opengl.GLES20.GL_TEXTURE_2D; -import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; -import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; -import static android.opengl.GLES20.GL_TRIANGLES; -import static android.opengl.GLES20.glActiveTexture; -import static android.opengl.GLES20.glBindTexture; -import static android.opengl.GLES20.glDrawArrays; -import static android.opengl.GLES20.glEnableVertexAttribArray; -import static android.opengl.GLES20.glGenTextures; -import static android.opengl.GLES20.glTexParameteri; -import static android.opengl.GLES20.glUniform1i; -import static android.opengl.GLES20.glVertexAttribPointer; - -import android.graphics.Bitmap; -import android.opengl.GLUtils; -import android.util.Log; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * This class takes charge of the geometry data like vertices and texture coordinates. - * It delivers these data to opengl runtime and triggers draw calls if necessary. - */ -class ImageGLWallpaper { - private static final String TAG = ImageGLWallpaper.class.getSimpleName(); - - static final String A_POSITION = "aPosition"; - static final String A_TEXTURE_COORDINATES = "aTextureCoordinates"; - static final String U_CENTER_REVEAL = "uCenterReveal"; - static final String U_REVEAL = "uReveal"; - static final String U_AOD2OPACITY = "uAod2Opacity"; - static final String U_TEXTURE = "uTexture"; - static final String U_AOD_MODE = "uAodMode"; - - private static final int HANDLE_UNDEFINED = -1; - private static final int POSITION_COMPONENT_COUNT = 2; - private static final int TEXTURE_COMPONENT_COUNT = 2; - private static final int BYTES_PER_FLOAT = 4; - - // Vertices to define the square with 2 triangles. - private static final float[] VERTICES = { - -1.0f, -1.0f, - +1.0f, -1.0f, - +1.0f, +1.0f, - +1.0f, +1.0f, - -1.0f, +1.0f, - -1.0f, -1.0f - }; - - // Texture coordinates that maps to vertices. - private static final float[] TEXTURES = { - 0f, 1f, - 1f, 1f, - 1f, 0f, - 1f, 0f, - 0f, 0f, - 0f, 1f - }; - - private final FloatBuffer mVertexBuffer; - private final FloatBuffer mTextureBuffer; - private final ImageGLProgram mProgram; - - private int mAttrPosition; - private int mAttrTextureCoordinates; - private int mUniAod2Opacity; - private int mUniAodMode; - private int mUniCenterReveal; - private int mUniReveal; - private int mUniTexture; - private int mTextureId; - - ImageGLWallpaper(ImageGLProgram program) { - mProgram = program; - - // Create an float array in opengles runtime (native) and put vertex data. - mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT) - .order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - mVertexBuffer.put(VERTICES); - mVertexBuffer.position(0); - - // Create an float array in opengles runtime (native) and put texture data. - mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT) - .order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - mTextureBuffer.put(TEXTURES); - mTextureBuffer.position(0); - } - - void setup() { - setupAttributes(); - setupUniforms(); - } - - private void setupAttributes() { - mAttrPosition = mProgram.getAttributeHandle(A_POSITION); - mVertexBuffer.position(0); - glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT, - false, 0, mVertexBuffer); - glEnableVertexAttribArray(mAttrPosition); - - mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES); - mTextureBuffer.position(0); - glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT, - false, 0, mTextureBuffer); - glEnableVertexAttribArray(mAttrTextureCoordinates); - } - - private void setupUniforms() { - mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY); - mUniAodMode = mProgram.getUniformHandle(U_AOD_MODE); - mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL); - mUniReveal = mProgram.getUniformHandle(U_REVEAL); - mUniTexture = mProgram.getUniformHandle(U_TEXTURE); - } - - int getHandle(String name) { - switch (name) { - case A_POSITION: - return mAttrPosition; - case A_TEXTURE_COORDINATES: - return mAttrTextureCoordinates; - case U_AOD2OPACITY: - return mUniAod2Opacity; - case U_AOD_MODE: - return mUniAodMode; - case U_CENTER_REVEAL: - return mUniCenterReveal; - case U_REVEAL: - return mUniReveal; - case U_TEXTURE: - return mUniTexture; - default: - return HANDLE_UNDEFINED; - } - } - - void draw() { - glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2); - } - - void setupTexture(Bitmap bitmap) { - final int[] tids = new int[1]; - - if (bitmap == null) { - Log.w(TAG, "setupTexture: invalid bitmap"); - return; - } - - // Generate one texture object and store the id in tids[0]. - glGenTextures(1, tids, 0); - if (tids[0] == 0) { - Log.w(TAG, "setupTexture: glGenTextures() failed"); - return; - } - - // Bind a named texture to a texturing target. - glBindTexture(GL_TEXTURE_2D, tids[0]); - // Load the bitmap data and copy it over into the texture object that is currently bound. - GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); - // Use bilinear texture filtering when minification. - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - // Use bilinear texture filtering when magnification. - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - mTextureId = tids[0]; - } - - void useTexture() { - // Set the active texture unit to texture unit 0. - glActiveTexture(GL_TEXTURE0); - // Bind the texture to this unit. - glBindTexture(GL_TEXTURE_2D, mTextureId); - // Let the texture sampler in fragment shader to read form this texture unit. - glUniform1i(mUniTexture, 0); - } - - void adjustTextureCoordinates(Bitmap bitmap, int surfaceWidth, int surfaceHeight, - float xOffset, float yOffset) { - if (bitmap == null) { - Log.d(TAG, "adjustTextureCoordinates: invalid bitmap"); - return; - } - - float ratioW = 1f; - float ratioH = 1f; - int bitmapWidth = bitmap.getWidth(); - int bitmapHeight = bitmap.getHeight(); - - boolean adjustWidth = bitmapWidth > surfaceWidth; - if (adjustWidth) { - ratioW = (float) surfaceWidth / bitmapWidth; - float referenceX = xOffset + ratioW > 1f ? 1f - ratioW : xOffset; - for (int i = 0; i < TEXTURES.length; i += 2) { - if (i == 2 || i == 4 || i == 6) { - TEXTURES[i] = Math.min(1f, referenceX + ratioW); - } else { - TEXTURES[i] = referenceX; - } - } - } - - boolean adjustHeight = bitmapHeight > surfaceHeight; - if (adjustHeight) { - ratioH = (float) surfaceHeight / bitmapHeight; - float referenceY = yOffset + ratioH > 1f ? 1f - ratioH : yOffset; - for (int i = 1; i < TEXTURES.length; i += 2) { - if (i == 1 || i == 3 || i == 11) { - TEXTURES[i] = Math.min(1f, referenceY + ratioH); - } else { - TEXTURES[i] = referenceY; - } - } - } - - if (adjustWidth || adjustHeight) { - mTextureBuffer.put(TEXTURES); - mTextureBuffer.position(0); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java deleted file mode 100644 index 477e7d7ebf72..000000000000 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +++ /dev/null @@ -1,144 +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.systemui.glwallpaper; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Handler.Callback; -import android.os.Message; -import android.util.Log; - -/** - * A helper class that computes histogram and percentile 85 from a bitmap. - * Percentile 85 will be computed each time the user picks a new image wallpaper. - */ -class ImageProcessHelper { - private static final String TAG = ImageProcessHelper.class.getSimpleName(); - private static final float DEFAULT_PER85 = 0.8f; - private static final int MSG_UPDATE_PER85 = 1; - - /** - * This color matrix will be applied to each pixel to get luminance from rgb by below formula: - * Luminance = .2126f * r + .7152f * g + .0722f * b. - */ - private static final float[] LUMINOSITY_MATRIX = new float[] { - .2126f, .0000f, .0000f, .0000f, .0000f, - .0000f, .7152f, .0000f, .0000f, .0000f, - .0000f, .0000f, .0722f, .0000f, .0000f, - .0000f, .0000f, .0000f, 1.000f, .0000f - }; - - private final Handler mHandler = new Handler(new Callback() { - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_PER85: - mPer85 = (float) msg.obj; - return true; - default: - return false; - } - } - }); - - private float mPer85 = DEFAULT_PER85; - - void startComputingPercentile85(Bitmap bitmap) { - new Per85ComputeTask(mHandler).execute(bitmap); - } - - float getPercentile85() { - return mPer85; - } - - private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> { - private Handler mUpdateHandler; - - Per85ComputeTask(Handler handler) { - super(handler); - mUpdateHandler = handler; - } - - @Override - protected Float doInBackground(Bitmap... bitmaps) { - Bitmap bitmap = bitmaps[0]; - if (bitmap != null) { - int[] histogram = processHistogram(bitmap); - return computePercentile85(bitmap, histogram); - } - Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); - return DEFAULT_PER85; - } - - @Override - protected void onPostExecute(Float result) { - Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result); - mUpdateHandler.sendMessage(msg); - } - - private int[] processHistogram(Bitmap bitmap) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - - Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig()); - Canvas canvas = new Canvas(target); - ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX); - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(cm)); - canvas.drawBitmap(bitmap, new Matrix(), paint); - - // TODO: Fine tune the performance here, tracking on b/123615079. - int[] histogram = new int[256]; - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - int pixel = target.getPixel(col, row); - int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel); - histogram[y]++; - } - } - - return histogram; - } - - private float computePercentile85(Bitmap bitmap, int[] histogram) { - float per85 = DEFAULT_PER85; - int pixelCount = bitmap.getWidth() * bitmap.getHeight(); - float[] acc = new float[256]; - for (int i = 0; i < acc.length; i++) { - acc[i] = (float) histogram[i] / pixelCount; - float prev = i == 0 ? 0f : acc[i - 1]; - float next = acc[i]; - float idx = (float) (i + 1) / 255; - float sum = prev + next; - if (prev < 0.85f && sum >= 0.85f) { - per85 = idx; - } - if (i > 0) { - acc[i] += acc[i - 1]; - } - } - return per85; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java deleted file mode 100644 index 787972c08e4a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ /dev/null @@ -1,103 +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.systemui.glwallpaper; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; - -import com.android.systemui.Interpolators; - -/** - * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition. - * The transition will happen while getting awake and quit events. - */ -class ImageRevealHelper { - private static final String TAG = ImageRevealHelper.class.getSimpleName(); - private static final float MAX_REVEAL = 0f; - private static final float MIN_REVEAL = 1f; - private static final int REVEAL_DURATION = 1000; - - private final ValueAnimator mAnimator; - private final RevealStateListener mRevealListener; - private float mReveal = MIN_REVEAL; - private boolean mAwake = false; - - ImageRevealHelper(RevealStateListener listener) { - mRevealListener = listener; - mAnimator = ValueAnimator.ofFloat(); - mAnimator.setDuration(REVEAL_DURATION); - mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mAnimator.addUpdateListener(animator -> { - mReveal = (float) animator.getAnimatedValue(); - if (mRevealListener != null) { - mRevealListener.onRevealStateChanged(); - } - }); - mAnimator.addListener(new AnimatorListenerAdapter() { - private boolean mIsCanceled; - - @Override - public void onAnimationCancel(Animator animation) { - mIsCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!mIsCanceled) { - mAwake = !mAwake; - } - mIsCanceled = false; - } - }); - } - - private void animate() { - mAnimator.cancel(); - mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL); - mAnimator.start(); - } - - public float getReveal() { - return mReveal; - } - - public boolean isAwake() { - return mAwake; - } - - void updateAwake(boolean awake) { - mAwake = awake; - animate(); - } - - void sleep() { - mReveal = MIN_REVEAL; - mAwake = false; - } - - /** - * A listener to trace value changes of reveal. - */ - public interface RevealStateListener { - - /** - * Called back while reveal status changes. - */ - void onRevealStateChanged(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java deleted file mode 100644 index 8916b28f201a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ /dev/null @@ -1,153 +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.systemui.glwallpaper; - -import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT; -import static android.opengl.GLES20.glClear; -import static android.opengl.GLES20.glClearColor; -import static android.opengl.GLES20.glUniform1f; -import static android.opengl.GLES20.glUniform1i; -import static android.opengl.GLES20.glViewport; - -import android.app.WallpaperManager; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.opengl.GLSurfaceView; -import android.util.Log; - -import com.android.systemui.ImageWallpaper; -import com.android.systemui.ImageWallpaper.ImageGLView; -import com.android.systemui.R; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -/** - * A GL renderer for image wallpaper. - */ -public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, - ImageWallpaper.SensorEventListener, ImageWallpaper.WallpaperStatusListener, - ImageRevealHelper.RevealStateListener { - private static final String TAG = ImageWallpaperRenderer.class.getSimpleName(); - - private final WallpaperManager mWallpaperManager; - private final ImageGLProgram mProgram; - private final ImageGLWallpaper mWallpaper; - private final ImageProcessHelper mImageProcessHelper; - private final ImageRevealHelper mImageRevealHelper; - private final ImageGLView mGLView; - private boolean mIsInAmbientMode; - private float mXOffset = 0f; - private float mYOffset = 0f; - - public ImageWallpaperRenderer(Context context, ImageGLView glView) { - mWallpaperManager = context.getSystemService(WallpaperManager.class); - if (mWallpaperManager == null) { - Log.w(TAG, "WallpaperManager not available"); - } - - mProgram = new ImageGLProgram(context); - mWallpaper = new ImageGLWallpaper(mProgram); - mImageProcessHelper = new ImageProcessHelper(); - mImageRevealHelper = new ImageRevealHelper(this); - mGLView = glView; - - if (mWallpaperManager != null) { - // Compute per85 as transition threshold, this is an async work. - mImageProcessHelper.startComputingPercentile85(mWallpaperManager.getBitmap()); - } - } - - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - glClearColor(0f, 0f, 0f, 1.0f); - mProgram.useGLProgram( - R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader); - mWallpaper.setup(); - mWallpaper.setupTexture(mWallpaperManager.getBitmap()); - } - - @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - glViewport(0, 0, width, height); - mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(), - width, height, mXOffset, mYOffset); - } - - @Override - public void onDrawFrame(GL10 gl) { - float threshold = mImageProcessHelper.getPercentile85(); - float reveal = mImageRevealHelper.getReveal(); - - glClear(GL_COLOR_BUFFER_BIT); - - glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), .25f); - glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold); - glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal); - glUniform1i(mWallpaper.getHandle(ImageGLWallpaper.U_AOD_MODE), mIsInAmbientMode ? 1 : 0); - - mWallpaper.useTexture(); - mWallpaper.draw(); - } - - @Override - public void onSensorEvent(boolean awake) { - mImageRevealHelper.updateAwake(awake); - } - - @Override - public void onAmbientModeChanged(boolean inAmbientMode) { - mIsInAmbientMode = inAmbientMode; - if (inAmbientMode) { - mImageRevealHelper.sleep(); - } - requestRender(); - } - - @Override - public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) { - if (frame == null || mWallpaperManager == null - || (xOffset == mXOffset && yOffset == mYOffset)) { - return; - } - - Bitmap bitmap = mWallpaperManager.getBitmap(); - if (bitmap == null) { - return; - } - - int width = frame.width(); - int height = frame.height(); - mXOffset = xOffset; - mYOffset = yOffset; - - mWallpaper.adjustTextureCoordinates(bitmap, width, height, mXOffset, mYOffset); - requestRender(); - } - - @Override - public void onRevealStateChanged() { - requestRender(); - } - - private void requestRender() { - if (mGLView != null) { - mGLView.render(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index c4c8bc703802..684175cf4212 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -51,8 +51,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; import com.android.systemui.statusbar.policy.ZenModeController; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 4527f73ab118..66cfadf9d6e8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -884,6 +884,7 @@ public class KeyguardViewMediator extends SystemUI { // Just to make sure, make sure the device is awake. mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_CAMERA_LAUNCH, "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK"); mPendingLock = false; mPendingReset = false; @@ -1854,8 +1855,9 @@ public class KeyguardViewMediator extends SystemUI { // It's possible that the device was unlocked in a dream state. It's time to wake up. if (mAodShowing) { - PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:BOUNCER_DOZING"); + PowerManager pm = mContext.getSystemService(PowerManager.class); + pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:BOUNCER_DOZING"); } synchronized (KeyguardViewMediator.this) { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 65ed889f34e1..ecbf0246a8ba 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -16,6 +16,7 @@ package com.android.systemui.privacy import android.content.Context import android.util.AttributeSet +import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout @@ -29,14 +30,25 @@ class OngoingPrivacyChip @JvmOverloads constructor( defStyleRes: Int = 0 ) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) { - private val iconMargin = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) + private val iconMarginExpanded = context.resources.getDimensionPixelSize( + R.dimen.ongoing_appops_chip_icon_margin_expanded) + private val iconMarginCollapsed = context.resources.getDimensionPixelSize( + R.dimen.ongoing_appops_chip_icon_margin_collapsed) private val iconSize = context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) - val iconColor = context.resources.getColor( + private val iconColor = context.resources.getColor( R.color.status_bar_clock_color, context.theme) + private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg) private lateinit var text: TextView private lateinit var iconsContainer: LinearLayout + private lateinit var inUseText: TextView + var expanded = false + set(value) { + if (value != field) { + field = value + updateView() + } + } var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>()) var privacyList = emptyList<PrivacyItem>() set(value) { @@ -48,15 +60,18 @@ class OngoingPrivacyChip @JvmOverloads constructor( override fun onFinishInflate() { super.onFinishInflate() + inUseText = findViewById(R.id.in_use_text) text = findViewById(R.id.text_container) iconsContainer = findViewById(R.id.icons_container) } // Should only be called if the builder icons or app changed private fun updateView() { + inUseText.visibility = if (expanded) View.GONE else View.VISIBLE + background = if (expanded) backgroundDrawable else null fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) { iconsContainer.removeAllViews() - dialogBuilder.generateIcons().forEach { + dialogBuilder.generateIcons().forEachIndexed { i, it -> it.mutate() it.setTint(iconColor) val image = ImageView(context).apply { @@ -64,17 +79,19 @@ class OngoingPrivacyChip @JvmOverloads constructor( scaleType = ImageView.ScaleType.CENTER_INSIDE } iconsContainer.addView(image, iconSize, iconSize) - val lp = image.layoutParams as MarginLayoutParams - lp.marginStart = iconMargin - image.layoutParams = lp + if (i != 0) { + val lp = image.layoutParams as MarginLayoutParams + lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed + image.layoutParams = lp + } } } if (!privacyList.isEmpty()) { generateContentDescription() setIcons(builder, iconsContainer) - text.visibility = if (builder.types.size == 1) VISIBLE else GONE - if (builder.types.size == 1) { + text.visibility = if (builder.types.size == 1 && expanded) VISIBLE else GONE + if (builder.types.size == 1 && expanded) { if (builder.app != null) { text.setText(builder.app?.applicationName) } else { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt index fa1426e45142..cff7fe449be1 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt @@ -69,7 +69,7 @@ class OngoingPrivacyDialog constructor( setPositiveButton(R.string.ongoing_privacy_dialog_ok, null) setNeutralButton(R.string.ongoing_privacy_dialog_open_settings, object : DialogInterface.OnClickListener { - val intent = Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS).putExtra( + val intent = Intent(Settings.ACTION_PRIVACY_SETTINGS).putExtra( Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1)) @Suppress("DEPRECATION") @@ -167,7 +167,7 @@ class OngoingPrivacyDialog constructor( // Check if package exists context.packageManager.getPackageInfo(app.packageName, 0) item.setOnClickListener(object : View.OnClickListener { - val intent = Intent(Intent.ACTION_REVIEW_APP_PERMISSION_USAGE) + val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS) .putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName) .putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(app.uid)) override fun onClick(v: View?) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index b865ce8d261a..f91c9d944439 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -189,6 +189,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateAnimator(right - left)); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + updateEverything(); } private void updateAnimator(int width) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 74e82b270aa0..ee9255c54a62 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -124,6 +124,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements private TintedIconManager mIconManager; private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; + private TouchAnimator mPrivacyChipAlphaAnimator; private View mSystemIconsView; private View mQuickQsStatusIcons; @@ -212,6 +213,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mNextAlarmTextView = findViewById(R.id.next_alarm_text); mRingerModeIcon = findViewById(R.id.ringer_mode_icon); mRingerModeTextView = findViewById(R.id.ringer_mode_text); + mPrivacyChip = findViewById(R.id.privacy_chip); + mPrivacyChip.setOnClickListener(this); updateResources(); @@ -230,8 +233,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mClockView = findViewById(R.id.clock); mClockView.setOnClickListener(this); mDateView = findViewById(R.id.date); - mPrivacyChip = findViewById(R.id.privacy_chip); - mPrivacyChip.setOnClickListener(this); mSpace = findViewById(R.id.space); // Tint for the battery icons are handled in setupHost() @@ -383,6 +384,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateStatusIconAlphaAnimator(); updateHeaderTextContainerAlphaAnimator(); + updatePrivacyChipAlphaAnimator(); } private void updateStatusIconAlphaAnimator() { @@ -398,6 +400,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements .build(); } + private void updatePrivacyChipAlphaAnimator() { + mPrivacyChipAlphaAnimator = new TouchAnimator.Builder() + .addFloat(mPrivacyChip, "alpha", 1, 0, 1) + .build(); + } + public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; mExpanded = expanded; @@ -431,6 +439,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (mHeaderTextContainerAlphaAnimator != null) { mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction); } + if (mPrivacyChipAlphaAnimator != null) { + mPrivacyChip.setExpanded(expansionFraction > 0.5); + mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction); + } // Check the original expansion fraction - we don't want to show the tooltip until the // panel is pulled all the way out. diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index e1a43785b5c4..e275690f70aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -58,10 +58,10 @@ import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.PagedTileLayout.TilePage; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QuickStatusBarHeader; -import com.android.systemui.statusbar.StatusBarStateController; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index de78d3376500..effa935c5c20 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Intent; import android.hardware.display.ColorDisplayManager; +import android.hardware.display.NightDisplayListener; import android.metrics.LogMaker; import android.provider.Settings; import android.service.quicksettings.Tile; @@ -31,7 +32,6 @@ import android.widget.Switch; import androidx.annotation.StringRes; -import com.android.internal.app.ColorDisplayController; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile.BooleanState; @@ -46,8 +46,9 @@ import java.util.TimeZone; import javax.inject.Inject; -public class NightDisplayTile extends QSTileImpl<BooleanState> - implements ColorDisplayController.Callback { +/** Quick settings tile: Night display **/ +public class NightDisplayTile extends QSTileImpl<BooleanState> implements + NightDisplayListener.Callback { /** * Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the @@ -57,13 +58,15 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> private static final String PATTERN_HOUR_MINUTE = "h:mm a"; private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm"; - private ColorDisplayController mController; + private final ColorDisplayManager mManager; + private NightDisplayListener mListener; private boolean mIsListening; @Inject public NightDisplayTile(QSHost host) { super(host); - mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser()); + mManager = mContext.getSystemService(ColorDisplayManager.class); + mListener = new NightDisplayListener(mContext, ActivityManager.getCurrentUser()); } @Override @@ -81,27 +84,27 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> // Enroll in forced auto mode if eligible. if ("1".equals(Settings.Global.getString(mContext.getContentResolver(), Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE)) - && mController.getAutoModeRaw() == -1) { - mController.setAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); + && mManager.getNightDisplayAutoModeRaw() == -1) { + mManager.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); Log.i("NightDisplayTile", "Enrolled in forced night display auto mode"); } // Change current activation state. final boolean activated = !mState.value; - mController.setActivated(activated); + mManager.setNightDisplayActivated(activated); } @Override protected void handleUserSwitch(int newUserId) { // Stop listening to the old controller. if (mIsListening) { - mController.setListener(null); + mListener.setCallback(null); } // Make a new controller for the new user. - mController = new ColorDisplayController(mContext, newUserId); + mListener = new NightDisplayListener(mContext, newUserId); if (mIsListening) { - mController.setListener(this); + mListener.setCallback(this); } super.handleUserSwitch(newUserId); @@ -109,7 +112,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> @Override protected void handleUpdateState(BooleanState state, Object arg) { - state.value = mController.isActivated(); + state.value = mManager.isNightDisplayActivated(); state.label = mContext.getString(R.string.quick_settings_night_display_label); state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_night_display_on); state.expandedAccessibilityClassName = Switch.class.getName(); @@ -121,12 +124,12 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> } /** - * Returns a {@link String} for the secondary label that reflects when the light will be turned - * on or off based on the current auto mode and night light activated status. + * Returns a String for the secondary label that reflects when the light will be turned on or + * off based on the current auto mode and night light activated status. */ @Nullable private String getSecondaryLabel(boolean isNightLightActivated) { - switch(mController.getAutoMode()) { + switch (mManager.getNightDisplayAutoMode()) { case ColorDisplayManager.AUTO_MODE_TWILIGHT: // Auto mode related to sunrise & sunset. If the light is on, it's guaranteed to be // turned off at sunrise. If it's off, it's guaranteed to be turned on at sunset. @@ -143,10 +146,10 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> final DateTimeFormatter toggleTimeFormat; if (isNightLightActivated) { - toggleTime = mController.getCustomEndTime(); + toggleTime = mManager.getNightDisplayCustomEndTime(); toggleTimeStringRes = R.string.quick_settings_secondary_label_until; } else { - toggleTime = mController.getCustomStartTime(); + toggleTime = mManager.getNightDisplayCustomStartTime(); toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at; } @@ -175,7 +178,8 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> @Override public LogMaker populate(LogMaker logMaker) { - return super.populate(logMaker).addTaggedData(FIELD_QS_MODE, mController.getAutoModeRaw()); + return super.populate(logMaker) + .addTaggedData(FIELD_QS_MODE, mManager.getNightDisplayAutoModeRaw()); } @Override @@ -187,10 +191,10 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> protected void handleSetListening(boolean listening) { mIsListening = listening; if (listening) { - mController.setListener(this); + mListener.setCallback(this); refreshState(); } else { - mController.setListener(null); + mListener.setCallback(null); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 2811505d738c..be749aef7f25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -47,7 +47,8 @@ import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 4f9d4282dae8..6a49b804f5ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -44,8 +44,9 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 98a3a547ed2c..490317b188c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -47,6 +47,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.Interpolators; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 491f310cedf2..b820dc09657a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -132,7 +132,8 @@ public class NotificationRemoteInputManager implements Dumpable { @Override public boolean onClickHandler( View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) { - mShadeController.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view); + mShadeController.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view, + "NOTIFICATION_CLICK"); if (handleRemoteInput(view, pendingIntent)) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 546b2e9df7b5..01b0bb14c7ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -26,7 +26,6 @@ import android.graphics.Rect; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.Log; -import android.util.MathUtils; import android.view.DisplayCutout; import android.view.View; import android.view.ViewGroup; @@ -38,7 +37,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -118,8 +118,8 @@ public class NotificationShelf extends ActivatableNotificationView implements @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - Dependency.get(StatusBarStateController.class) - .addCallback(this, StatusBarStateController.RANK_SHELF); + ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) + .addCallback(this, SysuiStatusBarStateController.RANK_SHELF); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index f2ff85bb226c..662cf514b977 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -61,7 +61,7 @@ public class NotificationViewHierarchyManager { protected final NotificationLockscreenUserManager mLockscreenUserManager; protected final NotificationGroupManager mGroupManager; protected final VisualStabilityManager mVisualStabilityManager; - private final StatusBarStateController mStatusBarStateController; + private final StatusBarStateControllerImpl mStatusBarStateController; private final NotificationEntryManager mEntryManager; // Lazy @@ -82,7 +82,7 @@ public class NotificationViewHierarchyManager { NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, - StatusBarStateController statusBarStateController, + StatusBarStateControllerImpl statusBarStateController, NotificationEntryManager notificationEntryManager, Lazy<ShadeController> shadeController) { mLockscreenUserManager = notificationLockscreenUserManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 54bce1c00354..ad5aa57f12ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -11,27 +11,22 @@ * 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.systemui.statusbar; -import static java.lang.annotation.RetentionPolicy.SOURCE; - import android.animation.ObjectAnimator; import android.animation.ValueAnimator; -import android.annotation.IntDef; import android.util.FloatProperty; import android.view.animation.Interpolator; import com.android.internal.annotations.GuardedBy; import com.android.systemui.Interpolators; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.CallbackController; -import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Comparator; @@ -42,24 +37,25 @@ import javax.inject.Singleton; * Tracks and reports on {@link StatusBarState}. */ @Singleton -public class StatusBarStateController implements CallbackController<StateListener> { +public class StatusBarStateControllerImpl implements SysuiStatusBarStateController, + CallbackController<StateListener> { private static final String TAG = "SbStateController"; private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER; private static final int MIN_STATE = StatusBarState.SHADE; - private static final Comparator <RankedListener> mComparator - = (o1, o2) -> Integer.compare(o1.rank, o2.rank); - private static final FloatProperty<StatusBarStateController> SET_DARK_AMOUNT_PROPERTY = - new FloatProperty<StatusBarStateController>("mDozeAmount") { + private static final Comparator<RankedListener> sComparator = + Comparator.comparingInt(o -> o.mRank); + private static final FloatProperty<StatusBarStateControllerImpl> SET_DARK_AMOUNT_PROPERTY = + new FloatProperty<StatusBarStateControllerImpl>("mDozeAmount") { @Override - public void setValue(StatusBarStateController object, float value) { + public void setValue(StatusBarStateControllerImpl object, float value) { object.setDozeAmountInternal(value); } @Override - public Float get(StatusBarStateController object) { + public Float get(StatusBarStateControllerImpl object) { return object.mDozeAmount; } }; @@ -95,29 +91,16 @@ public class StatusBarStateController implements CallbackController<StateListene */ private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN; - // TODO: b/115739177 (remove this explicit ordering if we can) - @Retention(SOURCE) - @IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF}) - public @interface SbStateListenerRank {} - // This is the set of known dependencies when updating StatusBarState - public static final int RANK_STATUS_BAR = 0; - public static final int RANK_STATUS_BAR_WINDOW_CONTROLLER = 1; - public static final int RANK_STACK_SCROLLER = 2; - public static final int RANK_SHELF = 3; - @Inject - public StatusBarStateController() { + public StatusBarStateControllerImpl() { } + @Override public int getState() { return mState; } - /** - * Update the status bar state - * @param state see {@link StatusBarState} for valid options - * @return {@code true} if the state changed, else {@code false} - */ + @Override public boolean setState(int state) { if (state > MAX_STATE || state < MIN_STATE) { throw new IllegalArgumentException("Invalid state " + state); @@ -127,40 +110,38 @@ public class StatusBarStateController implements CallbackController<StateListene } synchronized (mListeners) { for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.listener.onStatePreChange(mState, state); + rl.mListener.onStatePreChange(mState, state); } mLastState = mState; mState = state; for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.listener.onStateChanged(mState); + rl.mListener.onStateChanged(mState); } for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.listener.onStatePostChange(); + rl.mListener.onStatePostChange(); } } return true; } + @Override public boolean isDozing() { return mIsDozing; } + @Override public float getDozeAmount() { return mDozeAmount; } + @Override public float getInterpolatedDozeAmount() { return mDozeInterpolator.getInterpolation(mDozeAmount); } - /** - * Update the dozing state from {@link StatusBar}'s perspective - * @param isDozing well, are we dozing? - * @return {@code true} if the state changed, else {@code false} - */ - @SuppressWarnings("UnusedReturnValue") + @Override public boolean setIsDozing(boolean isDozing) { if (mIsDozing == isDozing) { return false; @@ -170,19 +151,14 @@ public class StatusBarStateController implements CallbackController<StateListene synchronized (mListeners) { for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.listener.onDozingChanged(isDozing); + rl.mListener.onDozingChanged(isDozing); } } return true; } - /** - * Changes the current doze amount. - * - * @param dozeAmount New doze/dark amount. - * @param animated If change should be animated or not. This will cancel current animations. - */ + @Override public void setDozeAmount(float dozeAmount, boolean animated) { if (mDarkAnimator != null && mDarkAnimator.isRunning()) { if (animated && mDozeAmountTarget == dozeAmount) { @@ -217,27 +193,32 @@ public class StatusBarStateController implements CallbackController<StateListene float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount); synchronized (mListeners) { for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.listener.onDozeAmountChanged(mDozeAmount, interpolatedAmount); + rl.mListener.onDozeAmountChanged(mDozeAmount, interpolatedAmount); } } } + @Override public boolean goingToFullShade() { return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide; } + @Override public void setLeaveOpenOnKeyguardHide(boolean leaveOpen) { mLeaveOpenOnKeyguardHide = leaveOpen; } + @Override public boolean leaveOpenOnKeyguardHide() { return mLeaveOpenOnKeyguardHide; } + @Override public boolean fromShadeLocked() { return mLastState == StatusBarState.SHADE_LOCKED; } + @Override public void addCallback(StateListener listener) { synchronized (mListeners) { addListenerInternalLocked(listener, Integer.MAX_VALUE); @@ -254,6 +235,8 @@ public class StatusBarStateController implements CallbackController<StateListene * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking * (i.e., they are non-dependent on the order of operations of StatusBarState listeners). */ + @Deprecated + @Override public void addCallback(StateListener listener, @SbStateListenerRank int rank) { synchronized (mListeners) { addListenerInternalLocked(listener, rank); @@ -264,91 +247,38 @@ public class StatusBarStateController implements CallbackController<StateListene private void addListenerInternalLocked(StateListener listener, int rank) { // Protect against double-subscribe for (RankedListener rl : mListeners) { - if (rl.listener.equals(listener)) { + if (rl.mListener.equals(listener)) { return; } } - RankedListener rl = new RankedListener(listener, rank); + RankedListener rl = new SysuiStatusBarStateController.RankedListener(listener, rank); mListeners.add(rl); - mListeners.sort(mComparator); + mListeners.sort(sComparator); } + + @Override public void removeCallback(StateListener listener) { synchronized (mListeners) { - mListeners.removeIf((it) -> it.listener.equals(listener)); + mListeners.removeIf((it) -> it.mListener.equals(listener)); } } + @Override public void setKeyguardRequested(boolean keyguardRequested) { mKeyguardRequested = keyguardRequested; } + @Override public boolean isKeyguardRequested() { return mKeyguardRequested; } - public static String describe(int state) { - return StatusBarState.toShortString(state); - } - - private class RankedListener { - private final StateListener listener; - private final int rank; - - private RankedListener(StateListener l, int r) { - listener = l; - rank = r; - } - } - /** - * Listener for StatusBarState updates + * Returns String readable state of status bar from {@link StatusBarState} */ - public interface StateListener { - - /** - * Callback before the new state is applied, for those who need to preempt the change. - * - * @param oldState state before the change - * @param newState new state to be applied in {@link #onStateChanged} - */ - public default void onStatePreChange(int oldState, int newState) { - } - - /** - * Callback after all listeners have had a chance to update based on the state change - */ - public default void onStatePostChange() { - } - - /** - * Required callback. Get the new state and do what you will with it. Keep in mind that - * other listeners are typically unordered and don't rely on your work being done before - * other peers. - * - * Only called if the state is actually different. - * - * @param newState the new {@link StatusBarState} - */ - default void onStateChanged(int newState) { - } - - /** - * Callback to be notified when Dozing changes. Dozing is stored separately from state. - * - * @param isDozing {@code true} if dozing according to {@link StatusBar} - */ - public default void onDozingChanged(boolean isDozing) {} - - /** - * Callback to be notified when the doze amount changes. Useful for animations. - * Note: this will be called for each animation frame. Please be careful to avoid - * performance regressions. - * - * @param linear A number from 0 to 1, where 1 means that the device is dozing. - * @param eased Same as {@code linear} but transformed by an interpolator. - */ - default void onDozeAmountChanged(float linear, float eased) {} + public static String describe(int state) { + return StatusBarState.toShortString(state); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java new file mode 100644 index 000000000000..dc5e1e913b4c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +import java.lang.annotation.Retention; + +/** + * Sends updates to {@link StateListener}s about changes to the status bar state and dozing state + */ +public interface SysuiStatusBarStateController extends StatusBarStateController { + + // TODO: b/115739177 (remove this explicit ordering if we can) + @Retention(SOURCE) + @IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF}) + @interface SbStateListenerRank {} + // This is the set of known dependencies when updating StatusBarState + int RANK_STATUS_BAR = 0; + int RANK_STATUS_BAR_WINDOW_CONTROLLER = 1; + int RANK_STACK_SCROLLER = 2; + int RANK_SHELF = 3; + + /** + * Add a listener and a rank based on the priority of this message + * @param listener the listener + * @param rank the order in which you'd like to be called. Ranked listeners will be + * notified before unranked, and we will sort ranked listeners from low to high + * + * @deprecated This method exists only to solve latent inter-dependencies from refactoring + * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking + * (i.e., they are non-dependent on the order of operations of StatusBarState listeners). + */ + @Deprecated + void addCallback(StateListener listener, int rank); + + /** + * Update the status bar state + * @param state see {@link StatusBarState} for valid options + * @return {@code true} if the state changed, else {@code false} + */ + boolean setState(int state); + + /** + * Update the dozing state from {@link StatusBar}'s perspective + * @param isDozing well, are we dozing? + * @return {@code true} if the state changed, else {@code false} + */ + boolean setIsDozing(boolean isDozing); + + /** + * Changes the current doze amount. + * + * @param dozeAmount New doze/dark amount. + * @param animated If change should be animated or not. This will cancel current animations. + */ + void setDozeAmount(float dozeAmount, boolean animated); + + /** + * Sets whether to leave status bar open when hiding keyguard + */ + void setLeaveOpenOnKeyguardHide(boolean leaveOpen); + + /** + * Whether to leave status bar open when hiding keyguard + */ + boolean leaveOpenOnKeyguardHide(); + + /** + * Interpolated doze amount + */ + float getInterpolatedDozeAmount(); + + /** + * Whether status bar is going to full shade + */ + boolean goingToFullShade(); + + /** + * Whether the previous state of the status bar was the shade locked + */ + boolean fromShadeLocked(); + + /** + * Set keyguard requested + */ + void setKeyguardRequested(boolean keyguardRequested); + + /** + * Is keyguard requested + */ + boolean isKeyguardRequested(); + + /** + * Listener with rankings SbStateListenerRank that have dependencies so must be updated + * in a certain order + */ + class RankedListener { + final StateListener mListener; + final int mRank; + + RankedListener(StateListener l, int r) { + mListener = l; + mRank = r; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index 49f1a8d6f248..b788f537316b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -52,7 +52,7 @@ public final class NotificationClicker implements View.OnClickListener { return; } - mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v); + mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; final StatusBarNotification sbn = row.getStatusBarNotification(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index c50f10b55a71..7cbe1a3e279e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -35,8 +35,8 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationPresenter; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index 54ed0d9cd8ce..8b522a2033f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.notification.collection; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.Person; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.SnoozeCriterion; @@ -235,8 +237,11 @@ public class NotificationData { if (mRankingMap != null) { getRanking(statusBarNotification.getKey(), mTmpRanking); if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT - || statusBarNotification.getNotification().isForegroundService() - || statusBarNotification.getNotification().hasMediaSession()) { + || isImportantOngoing(statusBarNotification.getNotification()) + || statusBarNotification.getNotification().hasMediaSession() + || hasPerson(statusBarNotification.getNotification()) + || hasStyle(statusBarNotification.getNotification(), + Notification.MessagingStyle.class)) { return true; } if (mGroupManager.isSummaryOfGroup(statusBarNotification)) { @@ -252,6 +257,24 @@ public class NotificationData { return false; } + private boolean isImportantOngoing(Notification notification) { + return notification.isForegroundService() + && mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_LOW; + } + + private boolean hasStyle(Notification notification, Class targetStyle) { + Class<? extends Notification.Style> style = notification.getNotificationStyle(); + return targetStyle.equals(style); + } + + private boolean hasPerson(Notification notification) { + // TODO: cache favorite and recent contacts to check contact affinity + ArrayList<Person> people = notification.extras != null + ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST) + : new ArrayList<>(); + return people != null && !people.isEmpty(); + } + public boolean isAmbient(String key) { if (mRankingMap != null) { getRanking(key, mTmpRanking); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 9f1693c71459..f74de5beb585 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -151,6 +151,12 @@ public final class NotificationEntry { private boolean mIsBubble; /** + * Whether this notification has been approved globally, at the app level, and at the channel + * level for bubbling. + */ + public boolean canBubble; + + /** * Whether this notification should be shown in the shade when it is also displayed as a bubble. * * <p>When a notification is a bubble we don't show it in the shade once the bubble has been @@ -197,6 +203,7 @@ public final class NotificationEntry { : ranking.getSmartReplies().toArray(new CharSequence[0]); suppressedVisualEffects = ranking.getSuppressedVisualEffects(); suspended = ranking.isSuspended(); + canBubble = ranking.canBubble(); } public void setInterruption() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 5e5241910e7f..c4ecb8259a4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -33,9 +33,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.UiOffloadThread; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index aa221993cd82..fea01ef61df9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -42,11 +42,11 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index b6948fc4bd1f..c7b2fab54fff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -537,7 +537,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mChosenImportance = IMPORTANCE_LOW; confirmationText.setText(R.string.notification_channel_silenced); } else { - mChosenImportance = IMPORTANCE_HIGH; + mChosenImportance = IMPORTANCE_DEFAULT; confirmationText.setText(R.string.notification_channel_unsilenced); } break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java index 4bdc1705c5ff..4c9c2f95b35c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java @@ -17,12 +17,7 @@ package com.android.systemui.statusbar.notification.row.wrapper; import android.content.Context; -import android.content.res.Configuration; import android.graphics.Color; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Paint; -import android.os.Build; import android.view.View; import com.android.internal.graphics.ColorUtils; @@ -49,43 +44,22 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { } @Override - public void onReinflated() { - super.onReinflated(); - - Configuration configuration = mView.getResources().getConfiguration(); - boolean nightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) - == Configuration.UI_MODE_NIGHT_YES; - - float[] hsl = new float[] {0f, 0f, 0f}; - ColorUtils.colorToHSL(mBackgroundColor, hsl); - boolean backgroundIsDark = Color.alpha(mBackgroundColor) == 0 - || hsl[1] == 0 && hsl[2] < 0.5; - boolean backgroundHasColor = hsl[1] > 0; + public void onContentUpdated(ExpandableNotificationRow row) { + super.onContentUpdated(row); // Let's invert the notification colors when we're in night mode and // the notification background isn't colorized. - if (!backgroundIsDark && !backgroundHasColor && nightMode - && mRow.getEntry().targetSdk < Build.VERSION_CODES.Q) { - Paint paint = new Paint(); - ColorMatrix matrix = new ColorMatrix(); - ColorMatrix tmp = new ColorMatrix(); - // Inversion should happen on Y'UV space to conseve the colors and - // only affect the luminosity. - matrix.setRGB2YUV(); - tmp.set(new float[]{ - -1f, 0f, 0f, 0f, 255f, - 0f, 1f, 0f, 0f, 0f, - 0f, 0f, 1f, 0f, 0f, - 0f, 0f, 0f, 1f, 0f - }); - matrix.postConcat(tmp); - tmp.setYUV2RGB(); - matrix.postConcat(tmp); - paint.setColorFilter(new ColorMatrixColorFilter(matrix)); - mView.setLayerType(View.LAYER_TYPE_HARDWARE, paint); - - hsl[2] = 1f - hsl[2]; - mBackgroundColor = ColorUtils.HSLToColor(hsl); + if (needsInversion(mBackgroundColor, mView)) { + invertViewLuminosity(mView); + + // Also invert background color if necessary + // (Otherwise we'd end-up with white on white.) + float[] hsl = new float[] {0f, 0f, 0f}; + ColorUtils.colorToHSL(mBackgroundColor, hsl); + if (mBackgroundColor != Color.TRANSPARENT && hsl[2] > 0.5) { + hsl[2] = 1f - hsl[2]; + mBackgroundColor = ColorUtils.HSLToColor(hsl); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java new file mode 100644 index 000000000000..49a8d56e1e65 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.wrapper; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; + +/** + * Wraps a notification containing a decorated custom view. + */ +public class NotificationDecoratedCustomViewWrapper extends NotificationTemplateViewWrapper { + + private View mWrappedView = null; + + protected NotificationDecoratedCustomViewWrapper(Context ctx, View view, + ExpandableNotificationRow row) { + super(ctx, view, row); + } + + @Override + public void onContentUpdated(ExpandableNotificationRow row) { + ViewGroup container = mView.findViewById( + com.android.internal.R.id.notification_main_column); + Integer childIndex = (Integer) container.getTag( + com.android.internal.R.id.notification_custom_view_index_tag); + if (childIndex != null && childIndex != -1) { + mWrappedView = container.getChildAt(childIndex); + } + if (needsInversion(resolveBackgroundColor(), mWrappedView)) { + invertViewLuminosity(mWrappedView); + } + super.onContentUpdated(row); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 9258c9971451..4c06ff6f5e49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -16,13 +16,22 @@ package com.android.systemui.statusbar.notification.row.wrapper; +import android.annotation.ColorInt; +import android.app.Notification; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.view.NotificationHeaderView; import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import com.android.internal.graphics.ColorUtils; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -50,6 +59,11 @@ public abstract class NotificationViewWrapper implements TransformableView { } else if ("messaging".equals(v.getTag())) { return new NotificationMessagingTemplateViewWrapper(ctx, v, row); } + Class<? extends Notification.Style> style = + row.getEntry().notification.getNotification().getNotificationStyle(); + if (Notification.DecoratedCustomViewStyle.class.equals(style)) { + return new NotificationDecoratedCustomViewWrapper(ctx, v, row); + } return new NotificationTemplateViewWrapper(ctx, v, row); } else if (v instanceof NotificationHeaderView) { return new NotificationHeaderViewWrapper(ctx, v, row); @@ -75,14 +89,110 @@ public abstract class NotificationViewWrapper implements TransformableView { if (shouldClearBackgroundOnReapply()) { mBackgroundColor = 0; } - Drawable background = mView.getBackground(); - if (background instanceof ColorDrawable) { - int backgroundColor = ((ColorDrawable) background).getColor(); - if (backgroundColor != Color.TRANSPARENT) { - mBackgroundColor = backgroundColor; - mView.setBackground(new ColorDrawable(Color.TRANSPARENT)); + int backgroundColor = getBackgroundColor(mView); + if (backgroundColor != Color.TRANSPARENT) { + mBackgroundColor = backgroundColor; + mView.setBackground(new ColorDrawable(Color.TRANSPARENT)); + } + } + + protected boolean needsInversion(int defaultBackgroundColor, View view) { + if (view == null) { + return false; + } + + Configuration configuration = mView.getResources().getConfiguration(); + boolean nightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + if (!nightMode) { + return false; + } + + int background = getBackgroundColor(view); + if (background == Color.TRANSPARENT) { + background = defaultBackgroundColor; + } + if (background == Color.TRANSPARENT) { + background = resolveBackgroundColor(); + } + + float[] hsl = new float[] {0f, 0f, 0f}; + ColorUtils.colorToHSL(background, hsl); + + // Notifications with colored backgrounds should not be inverted + if (hsl[1] != 0) { + return false; + } + + // Invert white or light gray backgrounds. + boolean isLightGrayOrWhite = hsl[1] == 0 && hsl[2] > 0.5; + if (isLightGrayOrWhite) { + return true; + } + + // Now let's check if there's unprotected text somewhere, and invert if we find it. + if (view instanceof ViewGroup) { + return childrenNeedInversion(background, (ViewGroup) view); + } else { + return false; + } + } + + private boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) { + if (viewGroup == null) { + return false; + } + + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + int backgroundColor = getBackgroundColor(viewGroup); + if (backgroundColor == Color.TRANSPARENT) { + backgroundColor = parentBackground; } + if (child instanceof TextView) { + int foreground = ((TextView) child).getCurrentTextColor(); + if (ColorUtils.calculateContrast(foreground, backgroundColor) < 3) { + return true; + } + } else if (child instanceof ViewGroup) { + if (childrenNeedInversion(backgroundColor, (ViewGroup) child)) { + return true; + } + } + } + + return false; + } + + protected int getBackgroundColor(View view) { + if (view == null) { + return Color.TRANSPARENT; } + Drawable background = view.getBackground(); + if (background instanceof ColorDrawable) { + return ((ColorDrawable) background).getColor(); + } + return Color.TRANSPARENT; + } + + protected void invertViewLuminosity(View view) { + Paint paint = new Paint(); + ColorMatrix matrix = new ColorMatrix(); + ColorMatrix tmp = new ColorMatrix(); + // Inversion should happen on Y'UV space to conserve the colors and + // only affect the luminosity. + matrix.setRGB2YUV(); + tmp.set(new float[]{ + -1f, 0f, 0f, 0f, 255f, + 0f, 1f, 0f, 0f, 0f, + 0f, 0f, 1f, 0f, 0f, + 0f, 0f, 0f, 1f, 0f + }); + matrix.postConcat(tmp); + tmp.setYUV2RGB(); + matrix.postConcat(tmp); + paint.setColorFilter(new ColorMatrixColorFilter(matrix)); + view.setLayerType(View.LAYER_TYPE_HARDWARE, paint); } protected boolean shouldClearBackgroundOnReapply() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 63b34d185c8d..7105876907bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -86,6 +86,8 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; import com.android.systemui.statusbar.EmptyShadeView; @@ -94,8 +96,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -599,6 +600,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd updateFooter(); } + @Override + public void onOverlayChanged() { + int newRadius = mContext.getResources().getDimensionPixelSize( + Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); + if (mCornerRadius != newRadius) { + mCornerRadius = newRadius; + invalidate(); + } + } + @VisibleForTesting @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void updateFooter() { @@ -654,8 +665,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void onAttachedToWindow() { super.onAttachedToWindow(); - Dependency.get(StatusBarStateController.class) - .addCallback(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER); + ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) + .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); Dependency.get(ConfigurationController.class).addCallback(this); } @@ -5420,7 +5431,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mHeadsUpAppearanceController.setPublicMode(publicMode); } - StatusBarStateController state = Dependency.get(StatusBarStateController.class); + SysuiStatusBarStateController state = (SysuiStatusBarStateController) + Dependency.get(StatusBarStateController.class); setHideSensitive(publicMode, state.goingToFullShade() /* animate */); setDimmed(onKeyguard, state.fromShadeLocked() /* animate */); setExpandingEnabled(!onKeyguard); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index b96c55b37c48..c9be2c8f6703 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.hardware.display.ColorDisplayManager; +import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.provider.Settings.Secure; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.ColorDisplayController; import com.android.systemui.Dependency; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; @@ -50,7 +50,7 @@ public class AutoTileManager { private final HotspotController mHotspotController; private final DataSaverController mDataSaverController; private final ManagedProfileController mManagedProfileController; - private final ColorDisplayController mColorDisplayController; + private final NightDisplayListener mNightDisplayListener; @Inject public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, @@ -58,7 +58,7 @@ public class AutoTileManager { HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, - ColorDisplayController colorDisplayController) { + NightDisplayListener nightDisplayListener) { mAutoTracker = autoAddTracker; mContext = context; mHost = host; @@ -66,7 +66,7 @@ public class AutoTileManager { mHotspotController = hotspotController; mDataSaverController = dataSaverController; mManagedProfileController = managedProfileController; - mColorDisplayController = colorDisplayController; + mNightDisplayListener = nightDisplayListener; if (!mAutoTracker.isAdded(HOTSPOT)) { hotspotController.addCallback(mHotspotCallback); } @@ -93,7 +93,7 @@ public class AutoTileManager { } if (!mAutoTracker.isAdded(NIGHT) && ColorDisplayManager.isNightDisplayAvailable(mContext)) { - colorDisplayController.setListener(mColorDisplayCallback); + nightDisplayListener.setCallback(mNightDisplayCallback); } } @@ -106,7 +106,7 @@ public class AutoTileManager { mDataSaverController.removeCallback(mDataSaverListener); mManagedProfileController.removeCallback(mProfileCallback); if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { - mColorDisplayController.setListener(null); + mNightDisplayListener.setCallback(null); } } @@ -157,8 +157,8 @@ public class AutoTileManager { }; @VisibleForTesting - final ColorDisplayController.Callback mColorDisplayCallback = - new ColorDisplayController.Callback() { + final NightDisplayListener.Callback mNightDisplayCallback = + new NightDisplayListener.Callback() { @Override public void onActivated(boolean activated) { if (activated) { @@ -178,7 +178,7 @@ public class AutoTileManager { if (mAutoTracker.isAdded(NIGHT)) return; mHost.addTile(NIGHT); mAutoTracker.setTileAdded(NIGHT); - mHandler.post(() -> mColorDisplayController.setListener(null)); + mHandler.post(() -> mNightDisplayListener.setCallback(null)); } }; } 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 304d2ee70c35..2162ea70fe84 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -232,7 +232,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { if (DEBUG_BIO_WAKELOCK) { Log.i(TAG, "bio wakelock: Authenticated, waking up..."); } - mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:BIOMETRIC"); + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + "android.policy:BIOMETRIC"); } if (delayWakeUp) { mKeyguardViewMediator.onWakeAndUnlocking(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 35763dce5529..6410860a852d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -33,8 +33,8 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardMonitor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 280dda0cd1dc..d9d74b9d17a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -24,8 +24,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; /** * Controller which handles all the doze animations of the scrims. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 035ccf1d6b81..4c1c0a4b4e58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -38,9 +38,9 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.ScreenDecorations; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index 1944c3f21444..5ccb9b294718 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -33,9 +33,9 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.Interpolators; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.KeyguardMonitor; import java.io.FileDescriptor; 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 ee047e41ec78..538d79717293 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -85,6 +85,7 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.assist.AssistManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -92,7 +93,6 @@ import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index d364356b19fe..b613e8efa8ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -25,12 +25,12 @@ import android.util.ArrayMap; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener; import com.android.systemui.statusbar.InflationTask; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index bb9e4183ba76..cc8af3ba64fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -22,11 +22,11 @@ import android.util.ArraySet; import android.util.Log; import com.android.systemui.Dependency; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 9e99fe9dac36..62f85fe8a00e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -11,6 +11,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.NonNull; +import androidx.collection.ArrayMap; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.ContrastColorUtil; @@ -19,10 +22,10 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -31,9 +34,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import java.util.ArrayList; import java.util.function.Function; -import androidx.annotation.NonNull; -import androidx.collection.ArrayMap; - /** * A controller for the space in the status bar to the left of the system icons. This area is * normally reserved for notifications. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index a9727c953afe..069703e9dd81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -17,8 +17,7 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.SysUiServiceProvider.getComponent; -import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator - .ExpandAnimationParameters; +import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -63,6 +62,8 @@ import com.android.systemui.classifier.FalsingManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.qs.QS; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.qs.QSFragment; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FlingAnimationUtils; @@ -73,8 +74,6 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.NotificationEntryManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index f17145d72be1..4d0c8c3a7fb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -44,9 +44,10 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.doze.DozeLog; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -142,8 +143,8 @@ public abstract class PanelView extends FrameLayout { private boolean mIgnoreXTouchSlop; private boolean mExpandLatencyTracking; protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); - protected final StatusBarStateController mStatusBarStateController = - Dependency.get(StatusBarStateController.class); + protected final SysuiStatusBarStateController mStatusBarStateController = + (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); protected void onExpandingFinished() { mBar.onExpandingFinished(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java index f9262186968f..234a968e9f75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java @@ -14,6 +14,7 @@ package com.android.systemui.statusbar.phone; +import android.annotation.NonNull; import android.view.View; import com.android.systemui.statusbar.StatusBarState; @@ -96,8 +97,9 @@ public interface ShadeController { * * @param time when to wake up * @param view the view requesting the wakeup + * @param why the reason for the wake up */ - void wakeUpIfDozing(long time, View view); + void wakeUpIfDozing(long time, View view, @NonNull String why); /** * If secure with redaction: Show bouncer, go to unlocked shade. 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 3532af560557..9f3bec60cb6d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -163,6 +163,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.Recents; @@ -188,7 +189,8 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -482,7 +484,8 @@ public class StatusBar extends SystemUI implements DemoMode, updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled); // If WallpaperInfo is null, it must be ImageWallpaper. final boolean supportsAmbientMode = deviceSupportsAodWallpaper - && (info == null || info.supportsAmbientMode()); + && (info == null && aodImageWallpaperEnabled + || info != null && info.supportsAmbientMode()); mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); @@ -551,14 +554,14 @@ public class StatusBar extends SystemUI implements DemoMode, private final View.OnClickListener mGoToLockedShadeListener = v -> { if (mState == StatusBarState.KEYGUARD) { - wakeUpIfDozing(SystemClock.uptimeMillis(), v); + wakeUpIfDozing(SystemClock.uptimeMillis(), v, "SHADE_CLICK"); goToLockedShade(null); } }; private boolean mNoAnimationOnNextBarModeChange; protected FalsingManager mFalsingManager; - private final StatusBarStateController - mStatusBarStateController = Dependency.get(StatusBarStateController.class); + private final SysuiStatusBarStateController mStatusBarStateController = + (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @@ -649,7 +652,8 @@ public class StatusBar extends SystemUI implements DemoMode, } mColorExtractor.addOnColorsChangedListener(this); - mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR); + mStatusBarStateController.addCallback(this, + StatusBarStateControllerImpl.RANK_STATUS_BAR); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mDreamManager = IDreamManager.Stub.asInterface( @@ -803,15 +807,17 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationLogger.setUpWithContainer(notifListContainer); mNotificationIconAreaController = SystemUIFactory.getInstance() - .createNotificationIconAreaController( - context, this, mStatusBarStateController, mNotificationListener); + .createNotificationIconAreaController(context, this, + mStatusBarStateController, mNotificationListener); inflateShelf(); mNotificationIconAreaController.setupShelf(mNotificationShelf); Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController); - // Allow plugins to reference DarkIconDispatcher + // Allow plugins to reference DarkIconDispatcher and StatusBarStateController Dependency.get(PluginDependencyProvider.class) .allowPluginDependency(DarkIconDispatcher.class); + Dependency.get(PluginDependencyProvider.class) + .allowPluginDependency(StatusBarStateController.class); FragmentHostManager.get(mStatusBarWindow) .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> { CollapsedStatusBarFragment statusBarFragment = @@ -1090,10 +1096,10 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void wakeUpIfDozing(long time, View where) { + public void wakeUpIfDozing(long time, View where, String why) { if (mDozing) { - PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - pm.wakeUp(time, "com.android.systemui:NODOZE"); + PowerManager pm = mContext.getSystemService(PowerManager.class); + pm.wakeUp(time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why); mWakeUpComingFromTouch = true; where.getLocationInWindow(mTmpInt2); mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, @@ -3369,7 +3375,8 @@ public class StatusBar extends SystemUI implements DemoMode, // ringing. // Other transitions are covered in handleVisibleToUserChanged(). if (mVisible && (newState == StatusBarState.SHADE_LOCKED - || (Dependency.get(StatusBarStateController.class).goingToFullShade()))) { + || (((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) + .goingToFullShade()))) { clearNotificationEffects(); } if (newState == StatusBarState.KEYGUARD) { @@ -3729,7 +3736,8 @@ public class StatusBar extends SystemUI implements DemoMode, } if (!mDeviceInteractive) { PowerManager pm = mContext.getSystemService(PowerManager.class); - pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE"); + pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH, + "com.android.systemui:CAMERA_GESTURE"); mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); } vibrateForCameraGesture(); @@ -3890,7 +3898,8 @@ public class StatusBar extends SystemUI implements DemoMode, public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { mScrimController.setPulseReason(reason); if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) { - mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE"); + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:LONG_PRESS"); startAssist(new Bundle()); return; } 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 bb23608799f0..5014783c43b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -42,10 +42,11 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; import com.android.systemui.SystemUIFactory; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.RemoteInputController; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; @@ -701,7 +702,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean isGoingToNotificationShade() { - return Dependency.get(StatusBarStateController.class).leaveOpenOnKeyguardHide(); + return ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) + .leaveOpenOnKeyguardHide(); } public boolean isSecure(int userId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 86326beb8b00..b0e006d75f81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -51,13 +51,13 @@ import com.android.systemui.EventLogTags; import com.android.systemui.UiOffloadThread; import com.android.systemui.assist.AssistManager; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryListener; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index df7f53b3b63a..e9705ff35a4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -48,6 +48,7 @@ import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -56,7 +57,7 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationAlertingManager; @@ -92,8 +93,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, Dependency.get(NotificationViewHierarchyManager.class); private final NotificationLockscreenUserManager mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); - private final StatusBarStateController mStatusBarStateController = - Dependency.get(StatusBarStateController.class); + private final SysuiStatusBarStateController mStatusBarStateController = + (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); private final NotificationRowBinder mNotificationRowBinder = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 8d5841088591..21d9dcc6897d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -35,13 +35,14 @@ import android.view.ViewParent; import com.android.systemui.Dependency; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -57,10 +58,10 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, StatusBarStateController.StateListener { private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); - private final StatusBarStateController mStatusBarStateController - = Dependency.get(StatusBarStateController.class); - private final NotificationLockscreenUserManager mLockscreenUserManager - = Dependency.get(NotificationLockscreenUserManager.class); + private final SysuiStatusBarStateController mStatusBarStateController = + (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); + private final NotificationLockscreenUserManager mLockscreenUserManager = + Dependency.get(NotificationLockscreenUserManager.class); private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); private final Context mContext; private View mPendingWorkRemoteInputView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 86e17f33fc77..e1a77b00f3ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -43,10 +43,11 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.RemoteInputController.Callback; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -98,8 +99,9 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat mDozeParameters = dozeParameters; mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze(); mLpChanged = new WindowManager.LayoutParams(); - Dependency.get(StatusBarStateController.class).addCallback( - mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER); + ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) + .addCallback(mStateListener, + SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER); Dependency.get(ConfigurationController.class).addCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 8b25c3469abe..ad4ba75d6569 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -59,15 +59,14 @@ import com.android.systemui.Dependency; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.io.FileDescriptor; import java.io.PrintWriter; - public class StatusBarWindowView extends FrameLayout { public static final String TAG = "StatusBarWindowView"; public static final boolean DEBUG = StatusBar.DEBUG; @@ -112,7 +111,7 @@ public class StatusBarWindowView extends FrameLayout { mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mFalsingManager = FalsingManager.getInstance(context); mDoubleTapHelper = new DoubleTapHelper(this, active -> {}, () -> { - mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this); + mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this, "DOUBLE_TAP"); return true; }, null, null); } diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java index dd1d0ca06fa8..6ee341dd974c 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java @@ -34,7 +34,7 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -156,8 +156,6 @@ public class AodMaskView extends ImageView implements StatusBarStateController.S private boolean checkIfNeedMask() { // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art). - // Because of conflicting with another wallpaper feature, - // we only support LockScreen wallpaper currently. return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop(); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index d80b444ad64b..5d03f19f4655 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -42,8 +42,8 @@ import android.widget.TextClock; import com.android.keyguard.clock.ClockManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ClockPlugin; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 49b4641399f1..2742577db860 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -294,11 +294,6 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mRow.getEntry().showInShadeWhenBubble()); } - @Test - public void testNotificationWithoutChannel() { - assertFalse(mBubbleController.shouldBubble(mNoChannelRow.getEntry())); - } - static class TestableBubbleController extends BubbleController { TestableBubbleController(Context context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index cfc19ef28c92..01d7b8b21b0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -44,8 +44,8 @@ import androidx.slice.core.SliceQuery; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; -import com.android.systemui.statusbar.StatusBarStateController; import org.junit.Assert; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index e5464e0343f4..86f6cd331fc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -47,10 +47,10 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import org.junit.Before; import org.junit.Ignore; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 56e1fc6b70de..62700c0b3b4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -95,7 +95,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, - mock(StatusBarStateController.class), mEntryManager, + mock(StatusBarStateControllerImpl.class), mEntryManager, () -> mShadeController); Dependency.get(InitController.class).executePostInitTasks(); mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index a0b342071b07..cad1a96b8075 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -170,7 +170,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 0, NotificationManager.IMPORTANCE_DEFAULT, null, null, - null, null, null, true, sentiment, false, -1, false, null, null); + null, null, null, true, sentiment, false, -1, false, null, null, false); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } @@ -189,7 +189,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { null, null, null, null, null, true, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1, - false, smartActions, null); + false, smartActions, null, false); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java index b507692b3eee..9f36a1eb9943 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java @@ -23,12 +23,19 @@ import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.CATEGORY_EVENT; import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.Notification.CATEGORY_REMINDER; +import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.app.NotificationManager.IMPORTANCE_MIN; + +import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_CHANNEL; +import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_IMPORTANCE; +import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_VIS_EFFECTS; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,6 +43,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.app.Notification; import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; import android.content.Intent; @@ -84,8 +92,6 @@ public class NotificationDataTest extends SysuiTestCase { private static final int UID_NORMAL = 123; private static final int UID_ALLOW_DURING_SETUP = 456; - private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey"; - private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt"; private static final NotificationChannel NOTIFICATION_CHANNEL = new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE); @@ -97,7 +103,7 @@ public class NotificationDataTest extends SysuiTestCase { NotificationData.KeyguardEnvironment mEnvironment; private final IPackageManager mMockPackageManager = mock(IPackageManager.class); - private NotificationData mNotificationData; + private TestableNotificationData mNotificationData; private ExpandableNotificationRow mRow; @Before @@ -131,6 +137,7 @@ public class NotificationDataTest extends SysuiTestCase { @Test public void testChannelSetWhenAdded() { + mNotificationData.rankingOverrides.putParcelable(OVERRIDE_CHANNEL, NOTIFICATION_CHANNEL); mNotificationData.add(mRow.getEntry()); assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel); } @@ -217,12 +224,12 @@ public class NotificationDataTest extends SysuiTestCase { @Test public void testIsExemptFromDndVisualSuppression_foreground() { initStatusBarNotification(false); - when(mMockStatusBarNotification.getKey()).thenReturn( - TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); + Notification n = mMockStatusBarNotification.getNotification(); n.flags = Notification.FLAG_FOREGROUND_SERVICE; NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); mNotificationData.add(entry); + mNotificationData.rankingOverrides.putInt(OVERRIDE_VIS_EFFECTS, 255); assertTrue(entry.isExemptFromDndVisualSuppression()); assertFalse(entry.shouldSuppressAmbient()); @@ -231,8 +238,6 @@ public class NotificationDataTest extends SysuiTestCase { @Test public void testIsExemptFromDndVisualSuppression_media() { initStatusBarNotification(false); - when(mMockStatusBarNotification.getKey()).thenReturn( - TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); Notification n = mMockStatusBarNotification.getNotification(); Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n); nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class))); @@ -240,6 +245,7 @@ public class NotificationDataTest extends SysuiTestCase { when(mMockStatusBarNotification.getNotification()).thenReturn(n); NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); mNotificationData.add(entry); + mNotificationData.rankingOverrides.putInt(OVERRIDE_VIS_EFFECTS, 255); assertTrue(entry.isExemptFromDndVisualSuppression()); assertFalse(entry.shouldSuppressAmbient()); @@ -248,11 +254,10 @@ public class NotificationDataTest extends SysuiTestCase { @Test public void testIsExemptFromDndVisualSuppression_system() { initStatusBarNotification(false); - when(mMockStatusBarNotification.getKey()).thenReturn( - TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); entry.mIsSystemNotification = true; mNotificationData.add(entry); + mNotificationData.rankingOverrides.putInt(OVERRIDE_VIS_EFFECTS, 255); assertTrue(entry.isExemptFromDndVisualSuppression()); assertFalse(entry.shouldSuppressAmbient()); @@ -261,10 +266,10 @@ public class NotificationDataTest extends SysuiTestCase { @Test public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() { initStatusBarNotification(false); - when(mMockStatusBarNotification.getKey()).thenReturn( - TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); entry.mIsSystemNotification = true; + mNotificationData.rankingOverrides.putInt(OVERRIDE_VIS_EFFECTS, + NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT); mNotificationData.add(entry); when(mMockStatusBarNotification.getNotification()).thenReturn( @@ -353,6 +358,64 @@ public class NotificationDataTest extends SysuiTestCase { assertTrue(entry.isLastMessageFromReply()); } + @Test + public void personHighPriority() { + Person person = new Person.Builder() + .setName("name") + .setKey("abc") + .setUri("uri") + .setBot(true) + .build(); + + Notification notification = new Notification.Builder(mContext, "test") + .addPerson(person) + .build(); + + StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, + notification, mContext.getUser(), "", 0); + + assertTrue(mNotificationData.isHighPriority(sbn)); + } + + @Test + public void messagingStyleHighPriority() { + + Notification notification = new Notification.Builder(mContext, "test") + .setStyle(new Notification.MessagingStyle("")) + .build(); + + StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, + notification, mContext.getUser(), "", 0); + + assertTrue(mNotificationData.isHighPriority(sbn)); + } + + @Test + public void minForegroundNotHighPriority() { + Notification notification = mock(Notification.class); + when(notification.isForegroundService()).thenReturn(true); + + mNotificationData.rankingOverrides.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_MIN); + + StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, + notification, mContext.getUser(), "", 0); + + assertFalse(mNotificationData.isHighPriority(sbn)); + } + + @Test + public void lowForegroundHighPriority() { + Notification notification = mock(Notification.class); + when(notification.isForegroundService()).thenReturn(true); + + mNotificationData.rankingOverrides.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW); + + StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, + notification, mContext.getUser(), "", 0); + + assertTrue(mNotificationData.isHighPriority(sbn)); + } + private void initStatusBarNotification(boolean allowDuringSetup) { Bundle bundle = new Bundle(); bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup); @@ -362,39 +425,90 @@ public class NotificationDataTest extends SysuiTestCase { when(mMockStatusBarNotification.getNotification()).thenReturn(notification); } - private class TestableNotificationData extends NotificationData { + public static class TestableNotificationData extends NotificationData { public TestableNotificationData() { super(); } + public static final String OVERRIDE_RANK = "r"; + public static final String OVERRIDE_DND = "dnd"; + public static final String OVERRIDE_VIS_OVERRIDE = "vo"; + public static final String OVERRIDE_VIS_EFFECTS = "ve"; + public static final String OVERRIDE_IMPORTANCE = "i"; + public static final String OVERRIDE_IMP_EXP = "ie"; + public static final String OVERRIDE_GROUP = "g"; + public static final String OVERRIDE_CHANNEL = "c"; + public static final String OVERRIDE_PEOPLE = "p"; + public static final String OVERRIDE_SNOOZE_CRITERIA = "sc"; + public static final String OVERRIDE_BADGE = "b"; + public static final String OVERRIDE_USER_SENTIMENT = "us"; + public static final String OVERRIDE_HIDDEN = "h"; + public static final String OVERRIDE_LAST_ALERTED = "la"; + public static final String OVERRIDE_NOISY = "n"; + public static final String OVERRIDE_SMART_ACTIONS = "sa"; + public static final String OVERRIDE_SMART_REPLIES = "sr"; + public static final String OVERRIDE_BUBBLE = "cb"; + + public Bundle rankingOverrides = new Bundle(); + @Override protected boolean getRanking(String key, Ranking outRanking) { super.getRanking(key, outRanking); - if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) { - outRanking.populate(key, outRanking.getRank(), - outRanking.matchesInterruptionFilter(), - outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(), - outRanking.getImportance(), outRanking.getImportanceExplanation(), - outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, - outRanking.canShowBadge(), outRanking.getUserSentiment(), true, - -1, false, null, null); - } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) { - outRanking.populate(key, outRanking.getRank(), - outRanking.matchesInterruptionFilter(), - outRanking.getVisibilityOverride(), 255, - outRanking.getImportance(), outRanking.getImportanceExplanation(), - outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, - outRanking.canShowBadge(), outRanking.getUserSentiment(), true, -1, - false, null, null); - } else { - outRanking.populate(key, outRanking.getRank(), - outRanking.matchesInterruptionFilter(), - outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(), - outRanking.getImportance(), outRanking.getImportanceExplanation(), - outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null, - outRanking.canShowBadge(), outRanking.getUserSentiment(), false, -1, - false, null, null); + + ArrayList<String> currentAdditionalPeople = new ArrayList<>(); + if (outRanking.getAdditionalPeople() != null) { + currentAdditionalPeople.addAll(outRanking.getAdditionalPeople()); + } + + ArrayList<SnoozeCriterion> currentSnooze = new ArrayList<>(); + if (outRanking.getSnoozeCriteria() != null) { + currentSnooze.addAll(outRanking.getSnoozeCriteria()); + } + + ArrayList<Notification.Action> currentActions = new ArrayList<>(); + if (outRanking.getSmartActions() != null) { + currentActions.addAll(outRanking.getSmartActions()); } + + ArrayList<CharSequence> currentReplies = new ArrayList<>(); + if (outRanking.getSmartReplies() != null) { + currentReplies.addAll(outRanking.getSmartReplies()); + } + + outRanking.populate(key, + rankingOverrides.getInt(OVERRIDE_RANK, outRanking.getRank()), + rankingOverrides.getBoolean(OVERRIDE_DND, + outRanking.matchesInterruptionFilter()), + rankingOverrides.getInt(OVERRIDE_VIS_OVERRIDE, + outRanking.getVisibilityOverride()), + rankingOverrides.getInt(OVERRIDE_VIS_EFFECTS, + outRanking.getSuppressedVisualEffects()), + rankingOverrides.getInt(OVERRIDE_IMPORTANCE, outRanking.getImportance()), + rankingOverrides.getCharSequence(OVERRIDE_IMP_EXP, + outRanking.getImportanceExplanation()), + rankingOverrides.getString(OVERRIDE_GROUP, outRanking.getOverrideGroupKey()), + rankingOverrides.containsKey(OVERRIDE_CHANNEL) + ? (NotificationChannel) rankingOverrides.getParcelable(OVERRIDE_CHANNEL) + : outRanking.getChannel(), + rankingOverrides.containsKey(OVERRIDE_PEOPLE) + ? rankingOverrides.getStringArrayList(OVERRIDE_PEOPLE) + : currentAdditionalPeople, + rankingOverrides.containsKey(OVERRIDE_SNOOZE_CRITERIA) + ? rankingOverrides.getParcelableArrayList(OVERRIDE_SNOOZE_CRITERIA) + : currentSnooze, + rankingOverrides.getBoolean(OVERRIDE_BADGE, outRanking.canShowBadge()), + rankingOverrides.getInt(OVERRIDE_USER_SENTIMENT, outRanking.getUserSentiment()), + rankingOverrides.getBoolean(OVERRIDE_HIDDEN, outRanking.isSuspended()), + rankingOverrides.getLong(OVERRIDE_LAST_ALERTED, + outRanking.getLastAudiblyAlertedMillis()), + rankingOverrides.getBoolean(OVERRIDE_NOISY, outRanking.isNoisy()), + rankingOverrides.containsKey(OVERRIDE_SMART_ACTIONS) + ? rankingOverrides.getParcelableArrayList(OVERRIDE_SMART_ACTIONS) + : currentActions, + rankingOverrides.containsKey(OVERRIDE_SMART_REPLIES) + ? rankingOverrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES) + : currentReplies, + rankingOverrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble())); return true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index db2706bee3c9..d47700b6f3e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -40,7 +40,7 @@ import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.UiOffloadThread; import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationData; @@ -99,7 +99,7 @@ public class NotificationLoggerTest extends SysuiTestCase { mEntry.setRow(mRow); mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class), - mEntryManager, mock(StatusBarStateController.class), mBarService, + mEntryManager, mock(StatusBarStateControllerImpl.class), mBarService, mExpansionStateLogger); mLogger.setUpWithContainer(mListContainer); verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture()); @@ -167,7 +167,7 @@ public class NotificationLoggerTest extends SysuiTestCase { TestableNotificationLogger(NotificationListener notificationListener, UiOffloadThread uiOffloadThread, NotificationEntryManager entryManager, - StatusBarStateController statusBarStateController, + StatusBarStateControllerImpl statusBarStateController, IStatusBarService barService, ExpansionStateLogger expansionStateLogger) { super(notificationListener, uiOffloadThread, entryManager, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 2a64445f342a..105bd9dd8793 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row; import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; @@ -1067,7 +1066,7 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), updated.capture()); assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); + assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance()); } @Test @@ -1111,7 +1110,7 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), updated.capture()); assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); + assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index ae70b01cd35c..d83508201529 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -51,13 +51,13 @@ import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index 1ded835e9651..f3740c4d51d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -21,13 +21,13 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.hardware.display.ColorDisplayManager; +import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import com.android.internal.app.ColorDisplayController; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; @@ -58,7 +58,7 @@ public class AutoTileManagerTest extends SysuiTestCase { mock(HotspotController.class), mock(DataSaverController.class), mock(ManagedProfileController.class), - mock(ColorDisplayController.class)); + mock(NightDisplayListener.class)); } @Test @@ -66,7 +66,7 @@ public class AutoTileManagerTest extends SysuiTestCase { if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { return; } - mAutoTileManager.mColorDisplayCallback.onActivated(true); + mAutoTileManager.mNightDisplayCallback.onActivated(true); verify(mQsTileHost).addTile("night"); } @@ -75,7 +75,7 @@ public class AutoTileManagerTest extends SysuiTestCase { if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { return; } - mAutoTileManager.mColorDisplayCallback.onActivated(false); + mAutoTileManager.mNightDisplayCallback.onActivated(false); verify(mQsTileHost, never()).addTile("night"); } @@ -84,7 +84,7 @@ public class AutoTileManagerTest extends SysuiTestCase { if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { return; } - mAutoTileManager.mColorDisplayCallback.onAutoModeChanged( + mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( ColorDisplayManager.AUTO_MODE_TWILIGHT); verify(mQsTileHost).addTile("night"); } @@ -94,7 +94,7 @@ public class AutoTileManagerTest extends SysuiTestCase { if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { return; } - mAutoTileManager.mColorDisplayCallback.onAutoModeChanged( + mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); verify(mQsTileHost).addTile("night"); } @@ -104,7 +104,7 @@ public class AutoTileManagerTest extends SysuiTestCase { if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { return; } - mAutoTileManager.mColorDisplayCallback.onAutoModeChanged( + mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( ColorDisplayManager.AUTO_MODE_DISABLED); verify(mQsTileHost, never()).addTile("night"); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index a72be7af8cd8..3c5425ce7d6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -32,8 +32,8 @@ import android.view.ViewPropertyAnimator; import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.tuner.TunerService; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java index 13145b830178..608dd8b0d281 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java @@ -20,23 +20,15 @@ import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MOD import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import android.content.Context; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 3b98f0ca8ce2..a97fa1ff1629 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -27,8 +27,9 @@ import android.testing.TestableLooper; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -46,7 +47,7 @@ import org.mockito.MockitoAnnotations; public class NotificationPanelViewTest extends SysuiTestCase { @Mock - private StatusBarStateController mStatusBarStateController; + private SysuiStatusBarStateController mStatusBarStateController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 036e57acb7e4..a95361f9e046 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -82,6 +82,7 @@ import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NavigationBarController; @@ -94,7 +95,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -153,7 +154,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; - @Mock private StatusBarStateController mStatusBarStateController; + @Mock private StatusBarStateControllerImpl mStatusBarStateController; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private NotificationPresenter mNotificationPresenter; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java index 46335dc3b5ec..11284d6ce261 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java @@ -28,9 +28,9 @@ import android.view.MotionEvent; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import org.junit.Before; diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index b8a57ae7b98f..1294dbbbfa87 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -16,17 +16,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := frameworks-base-overlays - LOCAL_REQUIRED_MODULES := \ - ExperimentNavigationBarFloatingOverlay \ - ExperimentNavigationBarDefaultOverlay \ - ExperimentNavigationBarSlimOverlay32 \ - ExperimentNavigationBarSlimOverlay40 \ - ExperimentNavigationBarLargeOverlay56 \ - ExperimentNavigationBarLargeOverlay64 \ AccentColorBlackOverlay \ AccentColorGreenOverlay \ AccentColorPurpleOverlay \ + DisplayCutoutEmulationCornerOverlay \ + DisplayCutoutEmulationDoubleOverlay \ + DisplayCutoutEmulationTallOverlay \ FontNotoSerifSourceOverlay \ IconPackCircularAndroidOverlay \ IconPackCircularSettingsOverlay \ @@ -42,7 +38,17 @@ LOCAL_REQUIRED_MODULES := \ IconShapeSquircleOverlay \ IconShapeTeardropOverlay - include $(BUILD_PHONY_PACKAGE) +include $(CLEAR_VARS) +LOCAL_MODULE := frameworks-base-overlays-debug +LOCAL_REQUIRED_MODULES := \ + ExperimentNavigationBarFloatingOverlay \ + ExperimentNavigationBarDefaultOverlay \ + ExperimentNavigationBarSlimOverlay32 \ + ExperimentNavigationBarSlimOverlay40 \ + ExperimentNavigationBarLargeOverlay56 \ + ExperimentNavigationBarLargeOverlay64 + +include $(BUILD_PHONY_PACKAGE) include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index eb8710d6759d..3c910696ba60 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6949,6 +6949,32 @@ message MetricsEvent { // OS: Q NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING = 1648; + // CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN + // Field to add the mimetype for a ChooserActivity + // OS:Q + FIELD_SHARESHEET_MIMETYPE = 1649; + + // CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN + // Sharesheet direct targets are ready to show. + // OS:Q + ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET = 1650; + + // CATEGORY: ACTION_SHARESHEET_SCROLL + // Sharesheet are either expanded, scrolling through them or compacted again. + // OS:Q + // Subtype 1 means collapsed, 0 expanded + ACTION_SHARESHEET_COLLAPSED_CHANGED = 1651; + + // ACTION: Share with screenshot extra + // OS: Q + ACTION_SHARE_WITH_PREVIEW = 1652; + + // CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN + // OS:Q + // The time elapsed from triggering the share to displaying the app targets + // formerly: histogram system_cost_for_smart_sharing + FIELD_TIME_TO_APP_TARGETS = 1653; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 2b45b49ff322..82359c547a88 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -515,6 +515,9 @@ message WifiLog { // Total number of scan results for WPA3-Enterprise networks optional int32 num_wpa3_enterprise_network_scan_results = 136; + + // WifiConfigStore read/write metrics. + optional WifiConfigStoreIO wifi_config_store_io = 137; } // Information that gets logged for every WiFi connection. @@ -668,6 +671,9 @@ message ConnectionEvent { // Has bug report been taken. optional bool automatic_bug_report_taken = 9; + + // Connection is using locally generated random MAC address. + optional bool use_randomized_mac = 10 [default = false]; } // Number of occurrences of a specific RSSI poll rssi value @@ -1874,6 +1880,13 @@ message WifiUsabilityStatsEntry { // Rx link speed at the sample time in Mbps optional int32 rx_link_speed_mbps = 27; + + // Sequence number generated by framework + optional int32 seq_num_inside_framework = 28; + + // Whether current entry is for the same BSSID on the same frequency compared + // to last entry + optional bool is_same_bssid_and_freq = 29; } message WifiUsabilityStats { @@ -2170,3 +2183,25 @@ message WifiDppLog { EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = 9; } } + +// WifiConfigStore read/write metrics. +message WifiConfigStoreIO { + // Histogram of config store read durations. + repeated DurationBucket read_durations = 1; + + // Histogram of config store write durations. + repeated DurationBucket write_durations = 2; + + // Total Number of instances of write/read duration in this duration bucket. + message DurationBucket { + // Bucket covers duration : [range_start_ms, range_end_ms) + // The (inclusive) lower bound of read/write duration represented by this bucket + optional int32 range_start_ms = 1; + + // The (exclusive) upper bound of read/write duration represented by this bucket + optional int32 range_end_ms = 2; + + // Number of read/write durations that fit into this bucket + optional int32 count = 3; + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 8886ee2365c0..1bce11ee95de 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -180,7 +180,7 @@ final class AutofillManagerServiceImpl mAugmentedAutofillResolver = new FrameworkResourcesServiceNameResolver(master.getContext(), com.android.internal.R.string.config_defaultAugmentedAutofillService); mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback( - (u, s) -> updateRemoteAugmentedAutofillService()); + (u, s) -> updateRemoteAugmentedAutofillService(s)); updateLocked(disabled); } @@ -1048,8 +1048,12 @@ final class AutofillManagerServiceImpl componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() { @Override public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) { - // TODO(b/123100811): properly implement Slog.w(TAG, "remote augmented autofill service died"); + final RemoteAugmentedAutofillService remoteService = + mRemoteAugmentedAutofillService; + if (remoteService != null) { + remoteService.destroy(); + } } }, mMaster.isInstantServiceAllowed(), mMaster.verbose); } @@ -1060,8 +1064,7 @@ final class AutofillManagerServiceImpl /** * Called when the {@link #mAugmentedAutofillResolver} changed (among other places). */ - private void updateRemoteAugmentedAutofillService() { - final String serviceName = mAugmentedAutofillResolver.getServiceName(mUserId); + private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) { if (serviceName == null) { if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!"); if (mRemoteAugmentedAutofillService != null) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 194332ab8451..04fc2588134e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2595,8 +2595,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int mode; if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) { mode = FLAG_SMART_SUGGESTION_SYSTEM; - } else if ((supportedModes & AutofillManager.FLAG_SMART_SUGGESTION_LEGACY) != 0) { - mode = AutofillManager.FLAG_SMART_SUGGESTION_LEGACY; } else { Slog.w(TAG, "Unsupported Smart Suggestion mode: " + supportedModes); return null; diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 14322ecd76b9..0dd1ded40ead 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -41,6 +41,7 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Trace; import android.os.UserHandle; +import android.os.UserManager; import android.util.Slog; import android.util.SparseArray; @@ -433,8 +434,46 @@ public class BackupManagerService { getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()"); return userBackupManagerService == null - ? null - : userBackupManagerService.getConfigurationIntent(transportName); + ? null + : userBackupManagerService.getConfigurationIntent(transportName); + } + + /** + * Sets the ancestral work profile for the calling user. + * + * <p> The ancestral work profile corresponds to the profile that was used to restore to the + * callers profile. + */ + public void setAncestralSerialNumber(long ancestralSerialNumber) { + UserBackupManagerService userBackupManagerService = + getServiceForUserIfCallerHasPermission( + Binder.getCallingUserHandle().getIdentifier(), + "setAncestralSerialNumber()"); + + if (userBackupManagerService != null) { + userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber); + } + } + + /** + * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the + * serial number of the its ancestral work profile. + * + * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)} + * and it corresponds to the profile that was used to restore to the callers profile. + */ + @Nullable + public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) { + for (UserHandle handle : mContext.getSystemService(UserManager.class).getUserProfiles()) { + UserBackupManagerService userBackupManagerService = getServiceUsers().get( + handle.getIdentifier()); + if (userBackupManagerService != null) { + if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) { + return handle; + } + } + } + return null; } /** diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 87872e8bcbc2..17e9b350d368 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -760,6 +760,21 @@ public class Trampoline extends IBackupManager.Stub { } @Override + @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) { + if (mService != null) { + return mService.getUserForAncestralSerialNumber(ancestralSerialNumber); + } + return null; + } + + @Override + public void setAncestralSerialNumber(long ancestralSerialNumber) { + if (mService != null) { + mService.setAncestralSerialNumber(ancestralSerialNumber); + } + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; int userId = binderGetCallingUserId(); diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 8b2c1b95ecfd..b2afbc3ec5f9 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -244,6 +244,8 @@ public class UserBackupManagerService { private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours + private static final String SERIAL_ID_FILE = "serial_id"; + private final @UserIdInt int mUserId; private final BackupAgentTimeoutParameters mAgentTimeoutParameters; private final TransportManager mTransportManager; @@ -360,6 +362,8 @@ public class UserBackupManagerService { private Set<String> mAncestralPackages = null; private long mAncestralToken = 0; private long mCurrentToken = 0; + @Nullable private File mAncestralSerialNumberFile; + /** * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This @@ -2308,6 +2312,55 @@ public class UserBackupManagerService { } } + /** + * Sets the work profile serial number of the ancestral work profile. + */ + public void setAncestralSerialNumber(long ancestralSerialNumber) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "setAncestralSerialNumber"); + Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber); + try (RandomAccessFile af = getAncestralSerialNumberFile()) { + af.writeLong(ancestralSerialNumber); + } catch (IOException e) { + Slog.w(TAG, "Unable to write to work profile serial mapping file:", e); + } + } + + /** + * Returns the work profile serial number of the ancestral device. This will be set by + * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set. + */ + public long getAncestralSerialNumber() { + try (RandomAccessFile af = getAncestralSerialNumberFile()) { + return af.readLong(); + } catch (IOException e) { + Slog.w(TAG, "Unable to write to work profile serial number file:", e); + return -1; + } + } + + private RandomAccessFile getAncestralSerialNumberFile() throws FileNotFoundException { + if (mAncestralSerialNumberFile == null) { + mAncestralSerialNumberFile = new File( + UserBackupManagerFiles.getBaseStateDir(getUserId()), + SERIAL_ID_FILE); + if (!mAncestralSerialNumberFile.exists()) { + try { + mAncestralSerialNumberFile.createNewFile(); + } catch (IOException e) { + Slog.w(TAG, "serial number mapping file creation failed", e); + } + } + } + return new RandomAccessFile(mAncestralSerialNumberFile, "rwd"); + } + + @VisibleForTesting + void setAncestralSerialNumberFile(File ancestralSerialNumberFile) { + mAncestralSerialNumberFile = ancestralSerialNumberFile; + } + + /** Clear the given package's backup data from the current transport. */ public void clearBackupData(String transportName, String packageName) { if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java b/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java new file mode 100644 index 000000000000..004d9e3b45f1 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java @@ -0,0 +1,80 @@ +/* + * 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.server.backup.encryption.chunking; + +import com.android.internal.util.Preconditions; + +/** Representation of a range of bytes to be downloaded. */ +final class ByteRange { + private final long mStart; + private final long mEnd; + + /** Creates a range of bytes which includes {@code mStart} and {@code mEnd}. */ + ByteRange(long start, long end) { + Preconditions.checkArgument(start >= 0); + Preconditions.checkArgument(end >= start); + mStart = start; + mEnd = end; + } + + /** Returns the start of the {@code ByteRange}. The start is included in the range. */ + long getStart() { + return mStart; + } + + /** Returns the end of the {@code ByteRange}. The end is included in the range. */ + long getEnd() { + return mEnd; + } + + /** Returns the number of bytes included in the {@code ByteRange}. */ + int getLength() { + return (int) (mEnd - mStart + 1); + } + + /** Creates a new {@link ByteRange} from {@code mStart} to {@code mEnd + length}. */ + ByteRange extend(long length) { + Preconditions.checkArgument(length > 0); + return new ByteRange(mStart, mEnd + length); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ByteRange)) { + return false; + } + + ByteRange byteRange = (ByteRange) o; + return (mEnd == byteRange.mEnd && mStart == byteRange.mStart); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (int) (mStart ^ (mStart >>> 32)); + result = 31 * result + (int) (mEnd ^ (mEnd >>> 32)); + return result; + } + + @Override + public String toString() { + return String.format("ByteRange{mStart=%d, mEnd=%d}", mStart, mEnd); + } +} diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java new file mode 100644 index 000000000000..69fb5cbf606d --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java @@ -0,0 +1,75 @@ +/* + * 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.server.backup.encryption.chunking; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.IOException; +import java.io.OutputStream; + +/** Writes backup data to a diff script, using a {@link SingleStreamDiffScriptWriter}. */ +public class DiffScriptBackupWriter implements BackupWriter { + /** + * The maximum size of a chunk in the diff script. The diff script writer {@code mWriter} will + * buffer this many bytes in memory. + */ + private static final int ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES = 1024 * 1024; + + private final SingleStreamDiffScriptWriter mWriter; + private long mBytesWritten; + + /** + * Constructs a new writer which writes the diff script to the given output stream, using the + * maximum new chunk size {@code ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES}. + */ + public static DiffScriptBackupWriter newInstance(OutputStream outputStream) { + SingleStreamDiffScriptWriter writer = + new SingleStreamDiffScriptWriter( + outputStream, ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES); + return new DiffScriptBackupWriter(writer); + } + + @VisibleForTesting + DiffScriptBackupWriter(SingleStreamDiffScriptWriter writer) { + mWriter = writer; + } + + @Override + public void writeBytes(byte[] bytes) throws IOException { + for (byte b : bytes) { + mWriter.writeByte(b); + } + + mBytesWritten += bytes.length; + } + + @Override + public void writeChunk(long start, int length) throws IOException { + mWriter.writeChunk(start, length); + mBytesWritten += length; + } + + @Override + public long getBytesWritten() { + return mBytesWritten; + } + + @Override + public void flush() throws IOException { + mWriter.flush(); + } +} diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java new file mode 100644 index 000000000000..49d15712d4cc --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java @@ -0,0 +1,36 @@ +/* + * 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.server.backup.encryption.chunking; + +import java.io.IOException; +import java.io.OutputStream; + +/** Writer that formats a Diff Script and writes it to an output source. */ +interface DiffScriptWriter { + /** Adds a new byte to the diff script. */ + void writeByte(byte b) throws IOException; + + /** Adds a known chunk to the diff script. */ + void writeChunk(long chunkStart, int chunkLength) throws IOException; + + /** Indicates that no more bytes or chunks will be added to the diff script. */ + void flush() throws IOException; + + interface Factory { + DiffScriptWriter create(OutputStream outputStream); + } +} diff --git a/telephony/java/android/telephony/ims/RcsPart.java b/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java index da501738a0bf..4aea60121810 100644 --- a/telephony/java/android/telephony/ims/RcsPart.java +++ b/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.telephony.ims; -import android.os.Parcelable; +package com.android.server.backup.encryption.chunking; -/** - * A part of a composite {@link RcsMessage}. - * @hide - TODO(sahinc) make this public - */ -public abstract class RcsPart implements Parcelable { +import java.io.OutputStream; + +/** An interface that wraps one {@link OutputStream} with another for filtration purposes. */ +public interface OutputStreamWrapper { + /** Wraps a given {@link OutputStream}. */ + OutputStream wrap(OutputStream outputStream); } diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java new file mode 100644 index 000000000000..0e4bd58345d5 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java @@ -0,0 +1,130 @@ +/* + * 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.server.backup.encryption.chunking; + +import android.annotation.Nullable; + +import com.android.internal.util.Preconditions; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.Locale; + +/** + * A {@link DiffScriptWriter} that writes an entire diff script to a single {@link OutputStream}. + */ +public class SingleStreamDiffScriptWriter implements DiffScriptWriter { + static final byte LINE_SEPARATOR = 0xA; + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + private final int mMaxNewByteChunkSize; + private final OutputStream mOutputStream; + private final byte[] mByteBuffer; + private int mBufferSize = 0; + // Each chunk could be written immediately to the output stream. However, + // it is possible that chunks may overlap. We therefore cache the most recent + // reusable chunk and try to merge it with future chunks. + private ByteRange mReusableChunk; + + public SingleStreamDiffScriptWriter(OutputStream outputStream, int maxNewByteChunkSize) { + mOutputStream = outputStream; + mMaxNewByteChunkSize = maxNewByteChunkSize; + mByteBuffer = new byte[maxNewByteChunkSize]; + } + + @Override + public void writeByte(byte b) throws IOException { + if (mReusableChunk != null) { + writeReusableChunk(); + } + mByteBuffer[mBufferSize++] = b; + if (mBufferSize == mMaxNewByteChunkSize) { + writeByteBuffer(); + } + } + + @Override + public void writeChunk(long chunkStart, int chunkLength) throws IOException { + Preconditions.checkArgument(chunkStart >= 0); + Preconditions.checkArgument(chunkLength > 0); + if (mBufferSize != 0) { + writeByteBuffer(); + } + + if (mReusableChunk != null && mReusableChunk.getEnd() + 1 == chunkStart) { + // The new chunk overlaps the old, so combine them into a single byte range. + mReusableChunk = mReusableChunk.extend(chunkLength); + } else { + writeReusableChunk(); + mReusableChunk = new ByteRange(chunkStart, chunkStart + chunkLength - 1); + } + } + + @Override + public void flush() throws IOException { + Preconditions.checkState(!(mBufferSize != 0 && mReusableChunk != null)); + if (mBufferSize != 0) { + writeByteBuffer(); + } + if (mReusableChunk != null) { + writeReusableChunk(); + } + mOutputStream.flush(); + } + + private void writeByteBuffer() throws IOException { + mOutputStream.write(Integer.toString(mBufferSize).getBytes(UTF_8)); + mOutputStream.write(LINE_SEPARATOR); + mOutputStream.write(mByteBuffer, 0, mBufferSize); + mOutputStream.write(LINE_SEPARATOR); + mBufferSize = 0; + } + + private void writeReusableChunk() throws IOException { + if (mReusableChunk != null) { + mOutputStream.write( + String.format( + Locale.US, + "%d-%d", + mReusableChunk.getStart(), + mReusableChunk.getEnd()) + .getBytes(UTF_8)); + mOutputStream.write(LINE_SEPARATOR); + mReusableChunk = null; + } + } + + /** A factory that creates {@link SingleStreamDiffScriptWriter}s. */ + public static class Factory implements DiffScriptWriter.Factory { + private final int mMaxNewByteChunkSize; + private final OutputStreamWrapper mOutputStreamWrapper; + + public Factory(int maxNewByteChunkSize, @Nullable OutputStreamWrapper outputStreamWrapper) { + mMaxNewByteChunkSize = maxNewByteChunkSize; + mOutputStreamWrapper = outputStreamWrapper; + } + + @Override + public SingleStreamDiffScriptWriter create(OutputStream outputStream) { + if (mOutputStreamWrapper != null) { + outputStream = mOutputStreamWrapper.wrap(outputStream); + } + return new SingleStreamDiffScriptWriter(outputStream, mMaxNewByteChunkSize); + } + } +} diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 9249c9cc023a..4afbc641ea6c 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; +import android.app.ActivityThread; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -39,6 +40,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; +import android.provider.DeviceConfig; import android.provider.Settings; import android.util.LocalLog; import android.util.Slog; @@ -89,19 +91,38 @@ public final class ContentCaptureManagerService extends @Nullable private SparseBooleanArray mDisabledUsers; + /** + * Global kill-switch based on value defined by + * {@link ContentCaptureManager#DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED}. + */ + @GuardedBy("mLock") + @Nullable + private boolean mDisabledByDeviceConfig; public ContentCaptureManagerService(@NonNull Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, com.android.internal.R.string.config_defaultContentCaptureService), UserManager.DISALLOW_CONTENT_CAPTURE); - // Sets which serviecs are disabled + DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ActivityThread.currentApplication().getMainExecutor(), + (namespace, key, value) -> { + if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED + .equals(key)) { + Slog.i(mTag, "Ignoring change on " + key); + return; + } + setDisabledByDeviceConfig(value); + }); + setDisabledByDeviceConfig(); + + // Sets which services are disabled final UserManager um = getContext().getSystemService(UserManager.class); final List<UserInfo> users = um.getUsers(); for (int i = 0; i < users.size(); i++) { final int userId = users.get(i).id; - final boolean disabled = isDisabledBySettings(userId); + final boolean disabled = mDisabledByDeviceConfig || isDisabledBySettings(userId); if (disabled) { - Slog.i(mTag, "user " + userId + " disabled by settings"); + Slog.i(mTag, "user " + userId + " disabled by settings or device config"); if (mDisabledUsers == null) { mDisabledUsers = new SparseBooleanArray(1); } @@ -160,7 +181,8 @@ public final class ContentCaptureManagerService extends @Override // from AbstractMasterSystemService protected boolean isDisabledLocked(@UserIdInt int userId) { - return isDisabledBySettingsLocked(userId) || super.isDisabledLocked(userId); + return mDisabledByDeviceConfig || isDisabledBySettingsLocked(userId) + || super.isDisabledLocked(userId); } private boolean isDisabledBySettingsLocked(@UserIdInt int userId) { @@ -191,6 +213,45 @@ public final class ContentCaptureManagerService extends return false; } + private void setDisabledByDeviceConfig() { + final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); + setDisabledByDeviceConfig(value); + } + + private void setDisabledByDeviceConfig(@Nullable String value) { + if (verbose) Slog.v(mTag, "setDisabledByDeviceConfig(): value=" + value); + final UserManager um = getContext().getSystemService(UserManager.class); + final List<UserInfo> users = um.getUsers(); + + final boolean newDisabledValue; + + if (value != null && value.equalsIgnoreCase("false")) { + newDisabledValue = true; + } else { + newDisabledValue = false; + } + + synchronized (mLock) { + if (mDisabledByDeviceConfig == newDisabledValue) { + if (verbose) { + Slog.v(mTag, "setDisabledByDeviceConfig(): already " + newDisabledValue); + } + return; + } + mDisabledByDeviceConfig = newDisabledValue; + + Slog.i(mTag, "setDisabledByDeviceConfig(): set to " + mDisabledByDeviceConfig); + for (int i = 0; i < users.size(); i++) { + final int userId = users.get(i).id; + boolean disabled = mDisabledByDeviceConfig || isDisabledBySettingsLocked(userId); + Slog.i(mTag, "setDisabledByDeviceConfig(): updating service for user " + + userId + " to " + (disabled ? "'disabled'" : "'enabled'")); + updateCachedServiceLocked(userId, disabled); + } + } + } + private void setContentCaptureFeatureEnabledForUser(@UserIdInt int userId, boolean enabled) { synchronized (mLock) { if (mDisabledUsers == null) { @@ -338,6 +399,8 @@ public final class ContentCaptureManagerService extends super.dumpLocked(prefix, pw); pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers); + pw.print(prefix); pw.print("Disabled by DeviceConfig: "); + pw.println(mDisabledByDeviceConfig); } final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub { @@ -406,7 +469,7 @@ public final class ContentCaptureManagerService extends "isContentCaptureFeatureEnabled()", userId, Binder.getCallingUid(), result); if (!isService) return; - enabled = !isDisabledBySettingsLocked(userId); + enabled = !mDisabledByDeviceConfig && !isDisabledBySettingsLocked(userId); } try { result.send(enabled ? ContentCaptureManager.RESULT_CODE_TRUE diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java index 2f78276bb533..39d5c9d0b777 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java @@ -77,6 +77,15 @@ public final class ContentCaptureManagerServiceShellCommand extends ShellCommand pw.println(" Temporarily (for DURATION ms) changes the service implemtation."); pw.println(" To reset, call with just the USER_ID argument."); pw.println(""); + pw.println(""); + pw.println(" set default-service-enabled USER_ID [true|false]"); + pw.println(" Enable / disable the default service for the user."); + pw.println(""); + pw.println(""); + pw.println(" get default-service-enabled USER_ID"); + pw.println(" Checks whether the default service is enabled for the user."); + pw.println(""); + pw.println(""); pw.println(" list sessions [--user USER_ID]"); pw.println(" Lists all pending sessions."); pw.println(""); @@ -91,6 +100,8 @@ public final class ContentCaptureManagerServiceShellCommand extends ShellCommand switch(what) { case "bind-instant-service-allowed": return getBindInstantService(pw); + case "default-service-enabled": + return getDefaultServiceEnabled(pw); default: pw.println("Invalid set: " + what); return -1; @@ -105,6 +116,8 @@ public final class ContentCaptureManagerServiceShellCommand extends ShellCommand return setBindInstantService(pw); case "temporary-service": return setTemporaryService(pw); + case "default-service-enabled": + return setDefaultServiceEnabled(); default: pw.println("Invalid set: " + what); return -1; @@ -149,6 +162,20 @@ public final class ContentCaptureManagerServiceShellCommand extends ShellCommand return 0; } + private int setDefaultServiceEnabled() { + final int userId = getNextIntArgRequired(); + final boolean enabled = Boolean.parseBoolean(getNextArg()); + mService.setDefaultServiceEnabled(userId, enabled); + return 0; + } + + private int getDefaultServiceEnabled(PrintWriter pw) { + final int userId = getNextIntArgRequired(); + final boolean enabled = mService.isDefaultServiceEnabled(userId); + pw.println(enabled); + return 0; + } + private int requestDestroy(PrintWriter pw) { if (!isNextArgSessions(pw)) { return -1; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1cb2c4a2f5ee..fe4411c1dbb5 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1833,14 +1833,20 @@ public class ConnectivityService extends IConnectivityManager.Stub "ConnectivityService"); } - private void enforceAnyPermissionOf(String... permissions) { + private boolean checkAnyPermissionOf(String... permissions) { for (String permission : permissions) { if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { - return; + return true; } } - throw new SecurityException( - "Requires one of the following permissions: " + String.join(", ", permissions) + "."); + return false; + } + + private void enforceAnyPermissionOf(String... permissions) { + if (!checkAnyPermissionOf(permissions)) { + throw new SecurityException("Requires one of the following permissions: " + + String.join(", ", permissions) + "."); + } } private void enforceInternetPermission() { @@ -1860,19 +1866,22 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceSettingsPermission() { - mContext.enforceCallingOrSelfPermission( + enforceAnyPermissionOf( android.Manifest.permission.NETWORK_SETTINGS, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkSettingsPermission() { - return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( - android.Manifest.permission.NETWORK_SETTINGS); + return checkAnyPermissionOf( + android.Manifest.permission.NETWORK_SETTINGS, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkSettingsPermission(int pid, int uid) { return PERMISSION_GRANTED == mContext.checkPermission( - android.Manifest.permission.NETWORK_SETTINGS, pid, uid); + android.Manifest.permission.NETWORK_SETTINGS, pid, uid) + || PERMISSION_GRANTED == mContext.checkPermission( + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid); } private void enforceTetherAccessPermission() { @@ -1882,9 +1891,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceConnectivityInternalPermission() { - mContext.enforceCallingOrSelfPermission( + enforceAnyPermissionOf( android.Manifest.permission.CONNECTIVITY_INTERNAL, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private void enforceControlAlwaysOnVpnPermission() { @@ -1895,20 +1904,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void enforceNetworkStackSettingsOrSetup() { enforceAnyPermissionOf( - android.Manifest.permission.NETWORK_SETTINGS, - android.Manifest.permission.NETWORK_SETUP_WIZARD, - android.Manifest.permission.NETWORK_STACK); - } - - private void enforceNetworkStackPermission() { - mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkNetworkStackPermission() { - return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( - android.Manifest.permission.NETWORK_STACK); + return checkAnyPermissionOf( + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private void enforceConnectivityRestrictedNetworksPermission() { @@ -3240,6 +3245,25 @@ public class ConnectivityService extends IConnectivityManager.Stub }); } + /** + * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this + * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself. + * @param appExtras Bundle to use as intent extras for the captive portal application. + * Must be treated as opaque to avoid preventing the captive portal app to + * update its arguments. + */ + @Override + public void startCaptivePortalAppInternal(Bundle appExtras) { + mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + + final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); + appIntent.putExtras(appExtras); + appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); + + Binder.withCleanCallingIdentity(() -> + mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); + } + public boolean avoidBadWifi() { return mMultinetworkPolicyTracker.getAvoidBadWifi(); } @@ -6377,6 +6401,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override + public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds, + Messenger messenger, IBinder binder) { + enforceKeepalivePermission(); + mKeepaliveTracker.startTcpKeepalive( + getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, messenger, binder); + } + + @Override public void stopKeepalive(Network network, int slot) { mHandler.sendMessage(mHandler.obtainMessage( NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, slot, SocketKeepalive.SUCCESS, network)); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 5278bbbb4af2..4834ce0da9b3 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -792,7 +792,7 @@ public class LocationManagerService extends ILocationManager.Stub { String[] testProviderStrings = resources.getStringArray( com.android.internal.R.array.config_testLocationProviders); for (String testProviderString : testProviderStrings) { - String fragments[] = testProviderString.split(","); + String[] fragments = testProviderString.split(","); String name = fragments[0].trim(); ProviderProperties properties = new ProviderProperties( Boolean.parseBoolean(fragments[1]) /* requiresNetwork */, @@ -816,12 +816,6 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - // this call has the side effect of forcing a write to the LOCATION_MODE setting in an OS - // upgrade case, and ensures that if anyone checks the LOCATION_MODE setting directly, they - // will see it in an appropriate state (at least after that user becomes foreground for the - // first time...) - isLocationEnabledForUser(userId); - // let providers know the current user is on the way out before changing the user for (LocationProvider p : mProviders) { p.onUserChangingLocked(); @@ -934,17 +928,22 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(mName + " provider:"); + pw.print(" " + mName + " provider"); if (isMock()) { - pw.println(" mock=true"); + pw.print(" [mock]"); } - pw.println(" attached=" + (mProvider != null)); - if (mIsManagedBySettings) { - pw.println(" allowed=" + mAllowed); + pw.println(":"); + + pw.println(" useable=" + mUseable); + if (!mUseable) { + pw.println(" attached=" + (mProvider != null)); + if (mIsManagedBySettings) { + pw.println(" allowed=" + mAllowed); + } + pw.println(" enabled=" + mEnabled); } - pw.println(" enabled=" + mEnabled); - pw.println(" useable=" + mUseable); - pw.println(" properties=" + mProperties); + + pw.println(" properties=" + mProperties); if (mProvider != null) { long identity = Binder.clearCallingIdentity(); @@ -1397,14 +1396,10 @@ public class LocationManagerService extends ILocationManager.Stub { public boolean callStatusChangedLocked(String provider, int status, Bundle extras) { if (mListener != null) { try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcastsLocked() - // is called before decrementPendingBroadcasts() - mListener.onStatusChanged(provider, status, extras); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + mListener.onStatusChanged(provider, status, extras); + // call this after broadcasting so we do not increment + // if we throw an exception. + incrementPendingBroadcastsLocked(); } catch (RemoteException e) { return false; } @@ -1413,16 +1408,12 @@ public class LocationManagerService extends ILocationManager.Stub { statusChanged.putExtras(new Bundle(extras)); statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status); try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcastsLocked() - // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, statusChanged, this, mHandler, - getResolutionPermission(mAllowedResolutionLevel), - PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + mPendingIntent.send(mContext, 0, statusChanged, this, mHandler, + getResolutionPermission(mAllowedResolutionLevel), + PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); + // call this after broadcasting so we do not increment + // if we throw an exception. + incrementPendingBroadcastsLocked(); } catch (PendingIntent.CanceledException e) { return false; } @@ -1433,14 +1424,10 @@ public class LocationManagerService extends ILocationManager.Stub { public boolean callLocationChangedLocked(Location location) { if (mListener != null) { try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcastsLocked() - // is called before decrementPendingBroadcasts() - mListener.onLocationChanged(new Location(location)); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + mListener.onLocationChanged(new Location(location)); + // call this after broadcasting so we do not increment + // if we throw an exception. + incrementPendingBroadcastsLocked(); } catch (RemoteException e) { return false; } @@ -1449,16 +1436,12 @@ public class LocationManagerService extends ILocationManager.Stub { locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location)); try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcastsLocked() - // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, locationChanged, this, mHandler, - getResolutionPermission(mAllowedResolutionLevel), - PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + mPendingIntent.send(mContext, 0, locationChanged, this, mHandler, + getResolutionPermission(mAllowedResolutionLevel), + PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); + // call this after broadcasting so we do not increment + // if we throw an exception. + incrementPendingBroadcastsLocked(); } catch (PendingIntent.CanceledException e) { return false; } @@ -1473,18 +1456,14 @@ public class LocationManagerService extends ILocationManager.Stub { if (mListener != null) { try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcastsLocked() - // is called before decrementPendingBroadcasts() - if (enabled) { - mListener.onProviderEnabled(provider); - } else { - mListener.onProviderDisabled(provider); - } - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); + if (enabled) { + mListener.onProviderEnabled(provider); + } else { + mListener.onProviderDisabled(provider); } + // call this after broadcasting so we do not increment + // if we throw an exception. + incrementPendingBroadcastsLocked(); } catch (RemoteException e) { return false; } @@ -1492,16 +1471,12 @@ public class LocationManagerService extends ILocationManager.Stub { Intent providerIntent = new Intent(); providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled); try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcastsLocked() - // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, providerIntent, this, mHandler, - getResolutionPermission(mAllowedResolutionLevel), - PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + mPendingIntent.send(mContext, 0, providerIntent, this, mHandler, + getResolutionPermission(mAllowedResolutionLevel), + PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); + // call this after broadcasting so we do not increment + // if we throw an exception. + incrementPendingBroadcastsLocked(); } catch (PendingIntent.CanceledException e) { return false; } @@ -1515,8 +1490,6 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { removeUpdatesLocked(this); - } - synchronized (this) { clearPendingBroadcastsLocked(); } } @@ -1524,7 +1497,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, String resultData, Bundle resultExtras) { - synchronized (this) { + synchronized (mLock) { decrementPendingBroadcastsLocked(); } } @@ -1533,13 +1506,25 @@ public class LocationManagerService extends ILocationManager.Stub { // containing the sending of the broadcaset private void incrementPendingBroadcastsLocked() { mPendingBroadcasts++; - mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS); + // so wakelock calls will succeed + long identity = Binder.clearCallingIdentity(); + try { + mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS); + } finally { + Binder.restoreCallingIdentity(identity); + } } private void decrementPendingBroadcastsLocked() { if (--mPendingBroadcasts == 0) { - if (mWakeLock.isHeld()) { - mWakeLock.release(); + // so wakelock calls will succeed + long identity = Binder.clearCallingIdentity(); + try { + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } finally { + Binder.restoreCallingIdentity(identity); } } } @@ -1547,8 +1532,14 @@ public class LocationManagerService extends ILocationManager.Stub { public void clearPendingBroadcastsLocked() { if (mPendingBroadcasts > 0) { mPendingBroadcasts = 0; - if (mWakeLock.isHeld()) { - mWakeLock.release(); + // so wakelock calls will succeed + long identity = Binder.clearCallingIdentity(); + try { + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } finally { + Binder.restoreCallingIdentity(identity); } } } @@ -1561,18 +1552,9 @@ public class LocationManagerService extends ILocationManager.Stub { //LocationListener was removed when it had a pending broadcast and should //not be added back. synchronized (mLock) { - IBinder binder = listener.asBinder(); - Receiver receiver = mReceivers.get(binder); + Receiver receiver = mReceivers.get(listener.asBinder()); if (receiver != null) { - synchronized (receiver) { - // so wakelock calls will succeed - long identity = Binder.clearCallingIdentity(); - try { - receiver.decrementPendingBroadcastsLocked(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } + receiver.decrementPendingBroadcastsLocked(); } } } @@ -2072,6 +2054,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (!provider.isUseableLocked()) { if (isSettingsExemptLocked(record)) { providerRequest.forceLocation = true; + providerRequest.lowPowerMode = false; } else { continue; } @@ -2080,7 +2063,9 @@ public class LocationManagerService extends ILocationManager.Stub { LocationRequest locationRequest = record.mRealRequest; long interval = locationRequest.getInterval(); - if (!isThrottlingExemptLocked(record.mReceiver.mCallerIdentity)) { + // if we're forcing location, don't apply any throttling + if (!providerRequest.forceLocation && !isThrottlingExemptLocked( + record.mReceiver.mCallerIdentity)) { if (!record.mIsForegroundUid) { interval = Math.max(interval, backgroundThrottleInterval); } @@ -2174,11 +2159,8 @@ public class LocationManagerService extends ILocationManager.Stub { return true; } - if (isProviderPackage(callerIdentity.mPackageName)) { - return true; - } + return isProviderPackage(callerIdentity.mPackageName); - return false; } @GuardedBy("mLock") @@ -2192,11 +2174,8 @@ public class LocationManagerService extends ILocationManager.Stub { return true; } - if (isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName)) { - return true; - } + return isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName); - return false; } private class UpdateRecord { @@ -2504,9 +2483,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { receiver.getListener().asBinder().unlinkToDeath(receiver, 0); - synchronized (receiver) { - receiver.clearPendingBroadcastsLocked(); - } + receiver.clearPendingBroadcastsLocked(); } receiver.updateMonitoring(false); @@ -2682,7 +2659,6 @@ public class LocationManagerService extends ILocationManager.Stub { // geo-fence manager uses the public location API, need to clear identity int uid = Binder.getCallingUid(); - // TODO: http://b/23822629 if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { // temporary measure until geofences work for secondary users Log.w(TAG, "proximity alerts are currently available only to the primary user"); @@ -2723,8 +2699,6 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } - // TODO(b/120449926): The GNSS status listeners should be handled similar to the GNSS - // measurements listeners. return mGnssStatusProvider.addListener(callback, new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName)); } @@ -2744,7 +2718,6 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - // TODO(b/120481270): Register for client death notification and update map. mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); try { @@ -2768,9 +2741,9 @@ public class LocationManagerService extends ILocationManager.Stub { android.Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to inject GNSS measurement corrections."); if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { - mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections); - } else { Slog.e(TAG, "Can not inject GNSS corrections due to no permission."); + } else { + mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections); } } @@ -2809,7 +2782,6 @@ public class LocationManagerService extends ILocationManager.Stub { CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - // TODO(b/120481270): Register for client death notification and update map. mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); try { @@ -3382,7 +3354,9 @@ public class LocationManagerService extends ILocationManager.Stub { return; } pw.println("Current Location Manager state:"); - pw.println(" Location Mode: " + isLocationEnabled()); + pw.println(" Current user: " + mCurrentUserId + " " + Arrays.toString( + mCurrentUserProfiles)); + pw.println(" Location mode: " + isLocationEnabled()); pw.println(" Location Listeners:"); for (Receiver receiver : mReceivers.values()) { pw.println(" " + receiver); @@ -3406,14 +3380,6 @@ public class LocationManagerService extends ILocationManager.Stub { + callerIdentity.mPackageName + ": " + isThrottlingExemptLocked(callerIdentity)); } - pw.println(" Overlay Provider Packages:"); - for (LocationProvider provider : mProviders) { - if (provider.mProvider instanceof LocationProviderProxy) { - pw.println(" " + provider.getName() + ": " - + ((LocationProviderProxy) provider.mProvider) - .getProviderPackages()); - } - } pw.println(" Historical Records by Provider:"); for (Map.Entry<PackageProviderKey, PackageStatistics> entry : mRequestStatistics.statistics.entrySet()) { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index f2329d3ebf8e..e7d7434b5dc8 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -4013,7 +4013,7 @@ class StorageManagerService extends IStorageManager.Stub return; } userPackages.add(packageName); - sandboxId = getSandboxId(packageName, sharedUserId); + sandboxId = StorageManagerService.this.getSandboxId(packageName, sharedUserId); } try { @@ -4028,7 +4028,8 @@ class StorageManagerService extends IStorageManager.Stub if (!ENABLE_ISOLATED_STORAGE) { return; } - final String sandboxId = getSandboxId(packageName, sharedUserId); + final String sandboxId = StorageManagerService.this.getSandboxId( + packageName, sharedUserId); synchronized (mPackagesLock) { final ArraySet<String> userPackages = mPackages.get(userId); // If the userPackages is null, it means the user is not started but we still @@ -4056,6 +4057,12 @@ class StorageManagerService extends IStorageManager.Stub return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]); } + @Override + public String getSandboxId(String packageName) { + return StorageManagerService.this.getSandboxId(packageName, + mPmInternal.getSharedUserIdForPackage(packageName)); + } + private String getVolumeLabel(VolumeInfo vol) { // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold switch (vol.getType()) { diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index c7b9a3cb7847..eaf790bb05c4 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -82,6 +82,9 @@ public class AdbDebuggingManager { private static final String ADB_DIRECTORY = "misc/adb"; // This file contains keys that will always be allowed to connect to the device via adb. private static final String ADB_KEYS_FILE = "adb_keys"; + // This file contains keys that will be allowed to connect without user interaction as long + // as a subsequent connection occurs within the allowed duration. + private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml"; private static final int BUFFER_SIZE = 4096; private final Context mContext; @@ -263,7 +266,6 @@ public class AdbDebuggingManager { AdbDebuggingHandler(Looper looper) { super(looper); - mAdbKeyStore = new AdbKeyStore(); } /** @@ -289,6 +291,7 @@ public class AdbDebuggingManager { mThread = new AdbDebuggingThread(); mThread.start(); + mAdbKeyStore = new AdbKeyStore(); break; case MESSAGE_ADB_DISABLED: @@ -303,6 +306,9 @@ public class AdbDebuggingManager { mThread = null; } + cancelJobToUpdateAdbKeyStore(); + mAdbKeyStore = null; + mConnectedKey = null; break; case MESSAGE_ADB_ALLOW: { @@ -532,7 +538,11 @@ public class AdbDebuggingManager { return new File(adbDir, fileName); } - private File getUserKeyFile() { + File getAdbTempKeysFile() { + return getAdbFile(ADB_TEMP_KEYS_FILE); + } + + File getUserKeyFile() { return getAdbFile(ADB_KEYS_FILE); } @@ -668,9 +678,6 @@ public class AdbDebuggingManager { private Map<String, Long> mKeyMap; private File mKeyFile; private AtomicFile mAtomicKeyFile; - // This file contains keys that will be allowed to connect without user interaction as long - // as a subsequent connection occurs within the allowed duration. - private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml"; private static final String XML_TAG_ADB_KEY = "adbKey"; private static final String XML_ATTRIBUTE_KEY = "key"; private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection"; @@ -704,9 +711,9 @@ public class AdbDebuggingManager { */ private void initKeyFile() { if (mKeyFile == null) { - mKeyFile = getAdbFile(ADB_TEMP_KEYS_FILE); + mKeyFile = getAdbTempKeysFile(); } - // getAdbFile can return null if the adb file cannot be obtained + // getAdbTempKeysFile can return null if the adb file cannot be obtained if (mKeyFile != null) { mAtomicKeyFile = new AtomicFile(mKeyFile); } @@ -767,7 +774,8 @@ public class AdbDebuggingManager { public void persistKeyStore() { // if there is nothing in the key map then ensure any keys left in the key store files // are deleted as well. - if (mKeyMap.size() == 0) { + filterOutOldKeys(); + if (mKeyMap.isEmpty()) { deleteKeyStore(); return; } @@ -784,22 +792,15 @@ public class AdbDebuggingManager { keyStream = mAtomicKeyFile.startWrite(); serializer.setOutput(keyStream, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); - long allowedTime = getAllowedConnectionTime(); - long systemTime = System.currentTimeMillis(); - Iterator keyMapIterator = mKeyMap.entrySet().iterator(); - while (keyMapIterator.hasNext()) { - Map.Entry<String, Long> keyEntry = (Map.Entry) keyMapIterator.next(); - long connectionTime = keyEntry.getValue(); - if (systemTime < (connectionTime + allowedTime)) { - serializer.startTag(null, XML_TAG_ADB_KEY); - serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); - serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION, - String.valueOf(keyEntry.getValue())); - serializer.endTag(null, XML_TAG_ADB_KEY); - } else { - keyMapIterator.remove(); - } + + for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { + serializer.startTag(null, XML_TAG_ADB_KEY); + serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); + serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION, + String.valueOf(keyEntry.getValue())); + serializer.endTag(null, XML_TAG_ADB_KEY); } + serializer.endDocument(); mAtomicKeyFile.finishWrite(keyStream); } catch (IOException e) { @@ -808,6 +809,19 @@ public class AdbDebuggingManager { } } + private void filterOutOldKeys() { + long allowedTime = getAllowedConnectionTime(); + long systemTime = System.currentTimeMillis(); + Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator(); + while (keyMapIterator.hasNext()) { + Map.Entry<String, Long> keyEntry = keyMapIterator.next(); + long connectionTime = keyEntry.getValue(); + if (allowedTime != 0 && systemTime > (connectionTime + allowedTime)) { + keyMapIterator.remove(); + } + } + } + /** * Removes all of the entries in the key map and deletes the key file. */ diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index c31691551269..7fd98e0043c9 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -45,6 +45,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; @@ -95,6 +96,16 @@ public class AdbService extends IAdbManager.Stub { public boolean isAdbEnabled() { return mAdbEnabled; } + + @Override + public File getAdbKeysFile() { + return mDebuggingManager.getUserKeyFile(); + } + + @Override + public File getAdbTempKeysFile() { + return mDebuggingManager.getAdbTempKeysFile(); + } } private final class AdbHandler extends Handler { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2f1f91e1734a..026430b5469d 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -333,7 +333,8 @@ public final class ActiveServices { } r.delayed = false; try { - startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true); + startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true, + false); } catch (TransactionTooLargeException e) { // Ignore, nobody upstack cares. } @@ -643,7 +644,8 @@ public final class ActiveServices { SERVICE_BG_ACTIVITY_START_TIMEOUT_MS); } } - ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); + ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting, + allowBackgroundActivityStarts); return cmp; } @@ -702,7 +704,8 @@ public final class ActiveServices { } ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, - boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { + boolean callerFg, boolean addToStarting, boolean allowBackgroundActivityStarts) + throws TransactionTooLargeException { ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity); @@ -713,7 +716,8 @@ public final class ActiveServices { synchronized (r.stats.getBatteryStats()) { r.stats.startRunningLocked(); } - String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false); + String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false, + allowBackgroundActivityStarts); if (error != null) { return new ComponentName("!!", error); } @@ -1645,7 +1649,7 @@ public final class ActiveServices { try { bringUpServiceLocked(serviceRecord, serviceIntent.getFlags(), - callerFg, false, false); + callerFg, false, false, false); } catch (RemoteException e) { /* ignore - local call */ } @@ -1748,7 +1752,7 @@ public final class ActiveServices { if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, - permissionsReviewRequired) != null) { + permissionsReviewRequired, false) != null) { return 0; } } @@ -2418,7 +2422,8 @@ public final class ActiveServices { return; } try { - bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false); + bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false, + false); } catch (TransactionTooLargeException e) { // Ignore, it's been logged and nothing upstack cares. } @@ -2463,8 +2468,8 @@ public final class ActiveServices { } private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, - boolean whileRestarting, boolean permissionsReviewRequired) - throws TransactionTooLargeException { + boolean whileRestarting, boolean permissionsReviewRequired, + boolean allowBackgroundActivityStarts) throws TransactionTooLargeException { //Slog.i(TAG, "Bring up service:"); //r.dump(" "); @@ -2575,6 +2580,13 @@ public final class ActiveServices { } } + if (app != null && allowBackgroundActivityStarts) { + app.addAllowBackgroundActivityStartsToken(r); + // schedule removal of the whitelisting token after the timeout + removeAllowBackgroundActivityStartsServiceToken(app, r, + SERVICE_BG_ACTIVITY_START_TIMEOUT_MS); + } + if (r.fgRequired) { if (DEBUG_FOREGROUND_SERVICE) { Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 94fc552fa1c2..60a45bfe04bb 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2226,7 +2226,7 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null; final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); mProcessList.init(this, activeUids); - mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, new Object()); + mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); mIntentFirewall = hasHandlerThread ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null; @@ -2274,7 +2274,7 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants = new ActivityManagerConstants(this, mHandler); final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */); mProcessList.init(this, activeUids); - mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, atm.getGlobalLock()); + mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); // Broadcast policy parameters final BroadcastConstants foreConstants = new BroadcastConstants( @@ -3495,7 +3495,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!app.killedByAm) { reportUidInfoMessageLocked(TAG, "Process " + app.processName + " (pid " + pid + ") has died: " - + ProcessList.makeOomAdjString(app.setAdj) + + ProcessList.makeOomAdjString(app.setAdj, true) + " " + ProcessList.makeProcStateString(app.setProcState), app.info.uid); mAllowLowerMemLevel = true; } else { @@ -6549,7 +6549,7 @@ public class ActivityManagerService extends IActivityManager.Stub conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, stable); if (conn != null && (conn.stableCount+conn.unstableCount) == 1) { - if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { + if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { // If this is a perceptible app accessing the provider, // make sure to count it as being accessed and thus // back up on the LRU list. This is good because @@ -10029,7 +10029,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(" #"); pw.print(index); pw.print(": "); - pw.print(ProcessList.makeOomAdjString(proc.setAdj)); + pw.print(ProcessList.makeOomAdjString(proc.setAdj, false)); pw.print(" "); pw.print(ProcessList.makeProcStateString(proc.getCurProcState())); pw.print(" "); @@ -10828,6 +10828,7 @@ public class ActivityManagerService extends IActivityManager.Stub printOomLevel(pw, "FOREGROUND_APP_ADJ", ProcessList.FOREGROUND_APP_ADJ); printOomLevel(pw, "VISIBLE_APP_ADJ", ProcessList.VISIBLE_APP_ADJ); printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", ProcessList.PERCEPTIBLE_APP_ADJ); + printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", ProcessList.PERCEPTIBLE_LOW_APP_ADJ); printOomLevel(pw, "BACKUP_APP_ADJ", ProcessList.BACKUP_APP_ADJ); printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", ProcessList.HEAVY_WEIGHT_APP_ADJ); printOomLevel(pw, "SERVICE_ADJ", ProcessList.SERVICE_ADJ); @@ -10878,16 +10879,17 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" Total number of kills: " + cnt); return reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MAX_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MIN_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_B_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.PREVIOUS_APP_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.HOME_APP_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.HEAVY_WEIGHT_APP_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.BACKUP_APP_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.PERCEPTIBLE_APP_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.VISIBLE_APP_ADJ) && - reportLmkKillAtOrBelow(pw, ProcessList.FOREGROUND_APP_ADJ); + reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MIN_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_B_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.PREVIOUS_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.HOME_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.HEAVY_WEIGHT_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.BACKUP_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.PERCEPTIBLE_LOW_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.PERCEPTIBLE_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.VISIBLE_APP_ADJ) && + reportLmkKillAtOrBelow(pw, ProcessList.FOREGROUND_APP_ADJ); } /** @@ -11334,7 +11336,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = list.size() - 1; i >= 0; i--) { ProcessRecord r = list.get(i).first; long token = proto.start(fieldId); - String oomAdj = ProcessList.makeOomAdjString(r.setAdj); + String oomAdj = ProcessList.makeOomAdjString(r.setAdj, true); proto.write(ProcessOomProto.PERSISTENT, r.isPersistent()); proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second); proto.write(ProcessOomProto.OOM_ADJ, oomAdj); @@ -11434,7 +11436,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i=list.size()-1; i>=0; i--) { ProcessRecord r = list.get(i).first; - String oomAdj = ProcessList.makeOomAdjString(r.setAdj); + String oomAdj = ProcessList.makeOomAdjString(r.setAdj, false); char schedGroup; switch (r.setSchedGroup) { case ProcessList.SCHED_GROUP_BACKGROUND: @@ -11760,7 +11762,8 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessList.NATIVE_ADJ, ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ, - ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, + ProcessList.VISIBLE_APP_ADJ, + ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ, ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ @@ -11768,7 +11771,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final String[] DUMP_MEM_OOM_LABEL = new String[] { "Native", "System", "Persistent", "Persistent Service", "Foreground", - "Visible", "Perceptible", + "Visible", "Perceptible", "Perceptible Low", "Heavy Weight", "Backup", "A Services", "Home", "Previous", "B Services", "Cached" @@ -11776,7 +11779,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { "native", "sys", "pers", "persvc", "fore", - "vis", "percept", + "vis", "percept", "perceptl", "heavy", "backup", "servicea", "home", "prev", "serviceb", "cached" @@ -12871,7 +12874,7 @@ public class ActivityManagerService extends IActivityManager.Stub private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss, long memtrack, String name) { sb.append(" "); - sb.append(ProcessList.makeOomAdjString(oomAdj)); + sb.append(ProcessList.makeOomAdjString(oomAdj, false)); sb.append(' '); sb.append(ProcessList.makeProcStateString(procState)); sb.append(' '); @@ -14997,7 +15000,7 @@ public class ActivityManagerService extends IActivityManager.Stub oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo, oldRecord.intent, Activity.RESULT_CANCELED, null, null, - false, false, oldRecord.userId); + false, false, oldRecord.userId, oldRecord); } catch (RemoteException e) { Slog.w(TAG, "Failure [" + queue.mQueueName + "] sending broadcast result of " diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index 1118014dab96..c7e4fc78c013 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -55,6 +55,8 @@ public final class AppCompactor { private static final int COMPACT_ACTION_FILE_FLAG = 1; private static final int COMPACT_ACTION_ANON_FLAG = 2; private static final int COMPACT_ACTION_FULL_FLAG = 3; + private static final int COMPACT_ACTION_NONE_FLAG = 4; + private static final String COMPACT_ACTION_NONE = ""; private static final String COMPACT_ACTION_FILE = "file"; private static final String COMPACT_ACTION_ANON = "anon"; private static final String COMPACT_ACTION_FULL = "all"; @@ -170,6 +172,9 @@ public final class AppCompactor { updateCompactionThrottles(); updateStatsdSampleRate(); } + Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(), + Process.THREAD_GROUP_SYSTEM); + } /** @@ -317,6 +322,8 @@ public final class AppCompactor { @VisibleForTesting static String compactActionIntToString(int action) { switch(action) { + case COMPACT_ACTION_NONE_FLAG: + return COMPACT_ACTION_NONE; case COMPACT_ACTION_FILE_FLAG: return COMPACT_ACTION_FILE; case COMPACT_ACTION_ANON_FLAG: @@ -324,7 +331,7 @@ public final class AppCompactor { case COMPACT_ACTION_FULL_FLAG: return COMPACT_ACTION_FULL; default: - return COMPACT_ACTION_FILE; + return COMPACT_ACTION_NONE; } } @@ -395,6 +402,10 @@ public final class AppCompactor { action = mCompactActionFull; } + if (action.equals(COMPACT_ACTION_NONE)) { + return; + } + try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index a11ebfd3e419..f0b137a6ccb1 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -74,6 +74,9 @@ public final class BroadcastQueue { static final int MAX_BROADCAST_SUMMARY_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 25 : 300; + // For how long after a whitelisted receiver's start its process can start a background activity + private static final int RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS = 10_000; + final ActivityManagerService mService; /** @@ -551,13 +554,23 @@ public final class BroadcastQueue { void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, - boolean ordered, boolean sticky, int sendingUser) throws RemoteException { + boolean ordered, boolean sticky, int sendingUser, BroadcastRecord br) + throws RemoteException { // Send the intent to the receiver asynchronously using one-way binder calls. if (app != null) { if (app.thread != null) { // If we have an app thread, do the call through that so it is // correctly ordered with other one-way calls. try { + if (br.allowBackgroundActivityStarts) { + app.addAllowBackgroundActivityStartsToken(br); + // schedule removal of the whitelisting token after the timeout + mHandler.postDelayed(() -> { + if (app != null) { + app.removeAllowBackgroundActivityStartsToken(br); + } + }, RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS); + } app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.getReportedProcState()); // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting @@ -783,7 +796,7 @@ public final class BroadcastQueue { } else { performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, - r.resultExtras, r.ordered, r.initialSticky, r.userId); + r.resultExtras, r.ordered, r.initialSticky, r.userId, r); } if (ordered) { r.state = BroadcastRecord.CALL_DONE_RECEIVE; @@ -1082,7 +1095,7 @@ public final class BroadcastQueue { } performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, - r.resultData, r.resultExtras, false, false, r.userId); + r.resultData, r.resultExtras, false, false, r.userId, r); // Set this to null so that the reference // (local and remote) isn't kept in the mBroadcastHistory. r.resultTo = null; diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 360d2960f61a..01946247bd12 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -56,6 +56,8 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class); sGlobalSettingToTypeMap.put( + Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, String.class); + sGlobalSettingToTypeMap.put( Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, String.class); sGlobalSettingToTypeMap.put( Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 03c5c93f2f20..93a71e5a2ed4 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -127,18 +127,8 @@ public final class OomAdjuster { private final ActivityManagerService mService; private final ProcessList mProcessList; - /** - * Used to lock {@link #updateOomAdjImpl} for state consistency. It also reduces frequency lock - * and unlock when getting and setting value to {@link ProcessRecord#mWindowProcessController}. - * Note it is declared as Object type so the locked-region-code-injection won't wrap the - * unnecessary priority booster. - */ - private final Object mAtmGlobalLock; - - OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, - Object atmGlobalLock) { + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { mService = service; - mAtmGlobalLock = atmGlobalLock; mProcessList = processList; mActiveUids = activeUids; @@ -196,13 +186,6 @@ public final class OomAdjuster { @GuardedBy("mService") final void updateOomAdjLocked() { - synchronized (mAtmGlobalLock) { - updateOomAdjImpl(); - } - } - - @GuardedBy({"mService", "mAtmGlobalLock"}) - private void updateOomAdjImpl() { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "updateOomAdj"); mService.mOomAdjProfiler.oomAdjStarted(); final ProcessRecord TOP_APP = mService.getTopAppLocked(); @@ -1224,8 +1207,8 @@ public final class OomAdjuster { } } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0 && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ - && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) { - newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1; + && adj > ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { + newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ; } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ && adj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -1610,7 +1593,7 @@ public final class OomAdjuster { // " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj); if (adj > app.maxAdj) { adj = app.maxAdj; - if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { + if (app.maxAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } @@ -1709,8 +1692,9 @@ public final class OomAdjuster { (app.curAdj == ProcessList.PREVIOUS_APP_ADJ || app.curAdj == ProcessList.HOME_APP_ADJ)) { mAppCompact.compactAppSome(app); - } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && - app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ + && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ + && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) { mAppCompact.compactAppFull(app); } } diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 0b27a8ad77dc..af56352b8cc0 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -384,6 +384,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { final boolean allowTrampoline = uid != callingUid && controller.mAtmInternal.isUidForeground(callingUid); + // note: we on purpose don't pass in the information about the PendingIntent's creator, + // like pid or ProcessRecord, to the ActivityTaskManagerInternal calls below, because + // it's not unusual for the creator's process to not be alive at this time switch (key.type) { case ActivityManager.INTENT_SENDER_ACTIVITY: try { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index f90c0cab984a..f01305eab761 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -183,6 +183,10 @@ public final class ProcessList { // is not entirely fatal but is generally a bad idea. static final int BACKUP_APP_ADJ = 300; + // This is a process bound by the system that's more important than services but not so + // perceptible that it affects the user immediately if killed. + static final int PERCEPTIBLE_LOW_APP_ADJ = 250; + // This is a process only hosting components that are perceptible to the // user, and we really want to avoid killing them, but they are not // immediately visible. An example is background music playback. @@ -679,47 +683,68 @@ public final class ProcessList { return totalProcessLimit/2; } - private static String buildOomTag(String prefix, String space, int val, int base) { + private static String buildOomTag(String prefix, String compactPrefix, String space, int val, + int base, boolean compact) { final int diff = val - base; if (diff == 0) { + if (compact) { + return compactPrefix; + } if (space == null) return prefix; return prefix + space; } if (diff < 10) { - return prefix + "+ " + Integer.toString(diff); + return prefix + (compact ? "+" : "+ ") + Integer.toString(diff); } return prefix + "+" + Integer.toString(diff); } - public static String makeOomAdjString(int setAdj) { + public static String makeOomAdjString(int setAdj, boolean compact) { if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { - return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ); + return buildOomTag("cch", "cch", " ", setAdj, + ProcessList.CACHED_APP_MIN_ADJ, compact); } else if (setAdj >= ProcessList.SERVICE_B_ADJ) { - return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ); + return buildOomTag("svcb ", "svcb", null, setAdj, + ProcessList.SERVICE_B_ADJ, compact); } else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) { - return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ); + return buildOomTag("prev ", "prev", null, setAdj, + ProcessList.PREVIOUS_APP_ADJ, compact); } else if (setAdj >= ProcessList.HOME_APP_ADJ) { - return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ); + return buildOomTag("home ", "home", null, setAdj, + ProcessList.HOME_APP_ADJ, compact); } else if (setAdj >= ProcessList.SERVICE_ADJ) { - return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ); + return buildOomTag("svc ", "svc", null, setAdj, + ProcessList.SERVICE_ADJ, compact); } else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { - return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); + return buildOomTag("hvy ", "hvy", null, setAdj, + ProcessList.HEAVY_WEIGHT_APP_ADJ, compact); } else if (setAdj >= ProcessList.BACKUP_APP_ADJ) { - return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ); + return buildOomTag("bkup ", "bkup", null, setAdj, + ProcessList.BACKUP_APP_ADJ, compact); + } else if (setAdj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { + return buildOomTag("prcl ", "prcl", null, setAdj, + ProcessList.PERCEPTIBLE_LOW_APP_ADJ, compact); } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { - return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ); + return buildOomTag("prcp ", "prcp", null, setAdj, + ProcessList.PERCEPTIBLE_APP_ADJ, compact); } else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) { - return buildOomTag("vis", " ", setAdj, ProcessList.VISIBLE_APP_ADJ); + return buildOomTag("vis", "vis", " ", setAdj, + ProcessList.VISIBLE_APP_ADJ, compact); } else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) { - return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ); + return buildOomTag("fore ", "fore", null, setAdj, + ProcessList.FOREGROUND_APP_ADJ, compact); } else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) { - return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ); + return buildOomTag("psvc ", "psvc", null, setAdj, + ProcessList.PERSISTENT_SERVICE_ADJ, compact); } else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) { - return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ); + return buildOomTag("pers ", "pers", null, setAdj, + ProcessList.PERSISTENT_PROC_ADJ, compact); } else if (setAdj >= ProcessList.SYSTEM_ADJ) { - return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ); + return buildOomTag("sys ", "sys", null, setAdj, + ProcessList.SYSTEM_ADJ, compact); } else if (setAdj >= ProcessList.NATIVE_ADJ) { - return buildOomTag("ntv ", null, setAdj, ProcessList.NATIVE_ADJ); + return buildOomTag("ntv ", "ntv", null, setAdj, + ProcessList.NATIVE_ADJ, compact); } else { return Integer.toString(setAdj); } @@ -1722,8 +1747,11 @@ public final class ProcessList { try { final String[] packageNames = mService.mContext.getPackageManager() .getPackagesForUid(uid); - final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class) + final StorageManagerInternal storageManagerInternal = + LocalServices.getService(StorageManagerInternal.class); + final String[] visibleVolIds = storageManagerInternal .getVisibleVolumesForUser(UserHandle.getUserId(uid)); + final String sandboxId = storageManagerInternal.getSandboxId(app.info.packageName); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkSlow(startTime, "startProcess: asking zygote to start proc"); @@ -1733,7 +1761,7 @@ public final class ProcessList { app.processName, uid, uid, gids, runtimeFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, - packageNames, visibleVolIds, + packageNames, visibleVolIds, sandboxId, new String[] {PROC_START_SEQ_IDENT + app.startSeq}); } else if (hostingType.equals("app_zygote")) { final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app); @@ -1742,14 +1770,14 @@ public final class ProcessList { app.processName, uid, uid, gids, runtimeFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, - packageNames, visibleVolIds, /*useBlastulaPool=*/ false, + packageNames, visibleVolIds, sandboxId, /*useBlastulaPool=*/ false, new String[] {PROC_START_SEQ_IDENT + app.startSeq}); } else { startResult = Process.start(entryPoint, app.processName, uid, uid, gids, runtimeFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, app.info.packageName, - packageNames, visibleVolIds, + packageNames, visibleVolIds, sandboxId, new String[] {PROC_START_SEQ_IDENT + app.startSeq}); } checkSlow(startTime, "startProcess: returned from zygote!"); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 5dccaf14e1f1..fcc857b7a75d 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -713,6 +713,8 @@ final class ProcessRecord implements WindowProcessListener { adj = ProcessList.VISIBLE_APP_ADJ; } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) { adj = ProcessList.PERCEPTIBLE_APP_ADJ; + } else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { + adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ; } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) { adj = ProcessList.CACHED_APP_MIN_ADJ; } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) { diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java index 3131255a61cd..0b6a4329d15b 100644 --- a/services/core/java/com/android/server/appbinding/AppBindingService.java +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -47,7 +47,7 @@ import com.android.internal.util.DumpUtils; import com.android.server.SystemService; import com.android.server.am.PersistentConnection; import com.android.server.appbinding.finders.AppServiceFinder; -import com.android.server.appbinding.finders.SmsAppServiceFinder; +import com.android.server.appbinding.finders.CarrierMessagingClientServiceFinder; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -147,7 +147,7 @@ public class AppBindingService extends Binder { mIPackageManager = injector.getIPackageManager(); mHandler = BackgroundThread.getHandler(); - mApps.add(new SmsAppServiceFinder(context, this::onAppChanged, mHandler)); + mApps.add(new CarrierMessagingClientServiceFinder(context, this::onAppChanged, mHandler)); // Initialize with the default value to make it non-null. mConstants = AppBindingConstants.initializeFromString(""); diff --git a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java index fcc28f8e2886..4c5f1a1c7b49 100644 --- a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java +++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java @@ -19,8 +19,6 @@ package com.android.server.appbinding.finders; import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL; import android.Manifest.permission; -import android.app.ISmsAppService; -import android.app.SmsAppService; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -30,6 +28,8 @@ import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; +import android.service.carrier.CarrierMessagingClientService; +import android.service.carrier.ICarrierMessagingClientService; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Slog; @@ -41,10 +41,11 @@ import com.android.server.appbinding.AppBindingConstants; import java.util.function.BiConsumer; /** - * Find the SmsAppService service within the default SMS app. + * Find the CarrierMessagingClientService service within the default SMS app. */ -public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsAppService> { - public SmsAppServiceFinder(Context context, +public class CarrierMessagingClientServiceFinder + extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> { + public CarrierMessagingClientServiceFinder(Context context, BiConsumer<AppServiceFinder, Integer> listener, Handler callbackHandler) { super(context, listener, callbackHandler); @@ -62,23 +63,23 @@ public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsApp } @Override - protected Class<SmsAppService> getServiceClass() { - return SmsAppService.class; + protected Class<CarrierMessagingClientService> getServiceClass() { + return CarrierMessagingClientService.class; } @Override - public ISmsAppService asInterface(IBinder obj) { - return ISmsAppService.Stub.asInterface(obj); + public ICarrierMessagingClientService asInterface(IBinder obj) { + return ICarrierMessagingClientService.Stub.asInterface(obj); } @Override protected String getServiceAction() { - return TelephonyManager.ACTION_SMS_APP_SERVICE; + return TelephonyManager.ACTION_CARRIER_MESSAGING_CLIENT_SERVICE; } @Override protected String getServicePermission() { - return permission.BIND_SMS_APP_SERVICE; + return permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE; } @Override @@ -121,7 +122,7 @@ public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsApp @Override public void onReceive(Context context, Intent intent) { if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) { - mListener.accept(SmsAppServiceFinder.this, getSendingUserId()); + mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId()); } } }; diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index a3bae521a162..6df60d6bdd3a 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -21,6 +21,8 @@ import static com.android.server.audio.AudioService.CONNECTION_STATE_DISCONNECTE import android.annotation.NonNull; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.ContentResolver; import android.content.Context; @@ -381,11 +383,11 @@ import java.util.ArrayList; //--------------------------------------------------------------------- // Message handling on behalf of helper classes - /*package*/ void broadcastScoConnectionState(int state) { + /*package*/ void postBroadcastScoConnectionState(int state) { sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state); } - /*package*/ void broadcastBecomingNoisy() { + /*package*/ void postBroadcastBecomingNoisy() { sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE); } @@ -415,6 +417,39 @@ import java.util.ArrayList; delay); } + /*package*/ void postDisconnectA2dp() { + sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); + } + + /*package*/ void postDisconnectA2dpSink() { + sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE); + } + + /*package*/ void postDisconnectHearingAid() { + sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); + } + + /*package*/ void postDisconnectHeadset() { + sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); + } + + /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) { + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile); + } + + /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) { + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile); + } + + /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) { + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile); + } + + /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) { + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE, + hearingAidProfile); + } + //--------------------------------------------------------------------- // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory) // only call from a "handle"* method or "on"* method @@ -444,33 +479,14 @@ import java.util.ArrayList; } } - /*package*/ void handleDisconnectA2dp() { - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dp(); - } - } - /*package*/ void handleDisconnectA2dpSink() { - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dpSink(); - } - } - - /*package*/ void handleDisconnectHearingAid() { - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectHearingAid(); - } - } - + @GuardedBy("mDeviceStateLock") /*package*/ void handleSetA2dpSinkConnectionState(@BluetoothProfile.BtProfileState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED; - final int delay; - synchronized (mDeviceStateLock) { - delay = mDeviceInventory.checkSendBecomingNoisyIntent( + final int delay = mDeviceInventory.checkSendBecomingNoisyIntent( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState, AudioSystem.DEVICE_NONE); - } final String addr = btDeviceInfo == null ? "null" : btDeviceInfo.getBtDevice().getAddress(); if (AudioService.DEBUG_DEVICES) { @@ -482,7 +498,7 @@ import java.util.ArrayList; state, btDeviceInfo, delay); } - /*package*/ void handleSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, + /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state, @@ -710,6 +726,46 @@ import java.util.ArrayList; (BtHelper.BluetoothA2dpDeviceInfo) msg.obj); } break; + case MSG_DISCONNECT_A2DP: + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectA2dp(); + } + break; + case MSG_DISCONNECT_A2DP_SINK: + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectA2dpSink(); + } + break; + case MSG_DISCONNECT_BT_HEARING_AID: + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectHearingAid(); + } + break; + case MSG_DISCONNECT_BT_HEADSET: + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } + break; + case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: + synchronized (mDeviceStateLock) { + mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); + } + break; + case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK: + synchronized (mDeviceStateLock) { + mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj); + } + break; + case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID: + synchronized (mDeviceStateLock) { + mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); + } + break; + case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } + break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -745,6 +801,14 @@ import java.util.ArrayList; private static final int MSG_I_DISCONNECT_BT_SCO = 16; private static final int MSG_TOGGLE_HDMI = 17; private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; + private static final int MSG_DISCONNECT_A2DP = 19; + private static final int MSG_DISCONNECT_A2DP_SINK = 20; + private static final int MSG_DISCONNECT_BT_HEARING_AID = 21; + private static final int MSG_DISCONNECT_BT_HEADSET = 22; + private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23; + private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24; + private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25; + private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26; private static boolean isMessageHandledUnderWakelock(int msgId) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 11fdc8f05033..37f0496c0db3 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -859,7 +859,7 @@ public final class AudioDeviceInventory { // also checks whether media routing if affected by a dynamic policy if (((device == musicDevice) || mDeviceBroker.isInCommunication()) && (device == devices) && !mDeviceBroker.hasMediaDynamicPolicy()) { - mDeviceBroker.broadcastBecomingNoisy(); + mDeviceBroker.postBroadcastBecomingNoisy(); delay = 1000; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index de63d0ef8edf..a6643d49c79f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5120,7 +5120,7 @@ public class AudioService extends IAudioService.Stub if (mUserSwitchedReceived) { // attempt to stop music playback for background user except on first user // switch (i.e. first boot) - mDeviceBroker.broadcastBecomingNoisy(); + mDeviceBroker.postBroadcastBecomingNoisy(); } mUserSwitchedReceived = true; // the current audio focus owner is no longer valid diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index b63af8a31cd9..58c1882abf6f 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -374,10 +374,10 @@ public class BtHelper { } /*package*/ synchronized void disconnectAllBluetoothProfiles() { - mDeviceBroker.handleDisconnectA2dp(); - mDeviceBroker.handleDisconnectA2dpSink(); - disconnectHeadset(); - mDeviceBroker.handleDisconnectHearingAid(); + mDeviceBroker.postDisconnectA2dp(); + mDeviceBroker.postDisconnectA2dpSink(); + mDeviceBroker.postDisconnectHeadset(); + mDeviceBroker.postDisconnectHearingAid(); } /*package*/ synchronized void resetBluetoothSco() { @@ -388,9 +388,92 @@ public class BtHelper { mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } + /*package*/ synchronized void disconnectHeadset() { + setBtScoActiveDevice(null); + mBluetoothHeadset = null; + } + + /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { + mA2dp = a2dp; + final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); + if (deviceList.isEmpty()) { + return; + } + final BluetoothDevice btDevice = deviceList.get(0); + final @BluetoothProfile.BtProfileState int state = mA2dp.getConnectionState(btDevice); + mDeviceBroker.handleSetA2dpSinkConnectionState( + state, new BluetoothA2dpDeviceInfo(btDevice)); + } + + /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { + final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); + if (deviceList.isEmpty()) { + return; + } + final BluetoothDevice btDevice = deviceList.get(0); + final @BluetoothProfile.BtProfileState int state = + profile.getConnectionState(btDevice); + mDeviceBroker.postSetA2dpSourceConnectionState( + state, new BluetoothA2dpDeviceInfo(btDevice)); + } + + /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { + mHearingAid = hearingAid; + final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); + if (deviceList.isEmpty()) { + return; + } + final BluetoothDevice btDevice = deviceList.get(0); + final @BluetoothProfile.BtProfileState int state = + mHearingAid.getConnectionState(btDevice); + mDeviceBroker.setBluetoothHearingAidDeviceConnectionState( + btDevice, state, + /*suppressNoisyIntent*/ false, + /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, + /*eventSource*/ "mBluetoothProfileServiceListener"); + } + + /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { + // Discard timeout message + mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); + mBluetoothHeadset = headset; + setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice()); + // Refresh SCO audio state + checkScoAudioState(); + if (mScoAudioState != SCO_STATE_ACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { + return; + } + boolean status = false; + if (mBluetoothHeadsetDevice != null) { + switch (mScoAudioState) { + case SCO_STATE_ACTIVATE_REQ: + status = connectBluetoothScoAudioHelper( + mBluetoothHeadset, + mBluetoothHeadsetDevice, mScoAudioMode); + if (status) { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + } + break; + case SCO_STATE_DEACTIVATE_REQ: + status = disconnectBluetoothScoAudioHelper( + mBluetoothHeadset, + mBluetoothHeadsetDevice, mScoAudioMode); + if (status) { + mScoAudioState = SCO_STATE_DEACTIVATING; + } + break; + } + } + if (!status) { + mScoAudioState = SCO_STATE_INACTIVE; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + } + //---------------------------------------------------------------------- private void broadcastScoConnectionState(int state) { - mDeviceBroker.broadcastScoConnectionState(state); + mDeviceBroker.postBroadcastScoConnectionState(state); } private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { @@ -457,99 +540,36 @@ public class BtHelper { } } + // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async + // methods inside listener. private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { - final BluetoothDevice btDevice; - List<BluetoothDevice> deviceList; switch(profile) { case BluetoothProfile.A2DP: - synchronized (BtHelper.this) { - mA2dp = (BluetoothA2dp) proxy; - deviceList = mA2dp.getConnectedDevices(); - if (deviceList.size() > 0) { - btDevice = deviceList.get(0); - if (btDevice == null) { - Log.e(TAG, "Invalid null device in BT profile listener"); - return; - } - final @BluetoothProfile.BtProfileState int state = - mA2dp.getConnectionState(btDevice); - mDeviceBroker.handleSetA2dpSinkConnectionState( - state, new BluetoothA2dpDeviceInfo(btDevice)); - } - } + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "BT profile service: connecting A2DP profile")); + mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); break; case BluetoothProfile.A2DP_SINK: - deviceList = proxy.getConnectedDevices(); - if (deviceList.size() > 0) { - btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - proxy.getConnectionState(btDevice); - mDeviceBroker.handleSetA2dpSourceConnectionState( - state, new BluetoothA2dpDeviceInfo(btDevice)); - } + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "BT profile service: connecting A2DP_SINK profile")); + mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); break; case BluetoothProfile.HEADSET: - synchronized (BtHelper.this) { - // Discard timeout message - mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); - mBluetoothHeadset = (BluetoothHeadset) proxy; - setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice()); - // Refresh SCO audio state - checkScoAudioState(); - // Continue pending action if any - if (mScoAudioState == SCO_STATE_ACTIVATE_REQ - || mScoAudioState == SCO_STATE_DEACTIVATE_REQ) { - boolean status = false; - if (mBluetoothHeadsetDevice != null) { - switch (mScoAudioState) { - case SCO_STATE_ACTIVATE_REQ: - status = connectBluetoothScoAudioHelper( - mBluetoothHeadset, - mBluetoothHeadsetDevice, mScoAudioMode); - if (status) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - } - break; - case SCO_STATE_DEACTIVATE_REQ: - status = disconnectBluetoothScoAudioHelper( - mBluetoothHeadset, - mBluetoothHeadsetDevice, mScoAudioMode); - if (status) { - mScoAudioState = SCO_STATE_DEACTIVATING; - } - break; - } - } - if (!status) { - mScoAudioState = SCO_STATE_INACTIVE; - broadcastScoConnectionState( - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - } - } - } + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "BT profile service: connecting HEADSET profile")); + mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); break; case BluetoothProfile.HEARING_AID: - synchronized (BtHelper.this) { - mHearingAid = (BluetoothHearingAid) proxy; - deviceList = mHearingAid.getConnectedDevices(); - if (deviceList.size() > 0) { - btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - mHearingAid.getConnectionState(btDevice); - mDeviceBroker.setBluetoothHearingAidDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, - /*eventSource*/ "mBluetoothProfileServiceListener"); - } - } + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "BT profile service: connecting HEARING_AID profile")); + mDeviceBroker.postBtHearingAidProfileConnected( + (BluetoothHearingAid) proxy); break; - default: break; } @@ -558,19 +578,19 @@ public class BtHelper { switch (profile) { case BluetoothProfile.A2DP: - mDeviceBroker.handleDisconnectA2dp(); + mDeviceBroker.postDisconnectA2dp(); break; case BluetoothProfile.A2DP_SINK: - mDeviceBroker.handleDisconnectA2dpSink(); + mDeviceBroker.postDisconnectA2dpSink(); break; case BluetoothProfile.HEADSET: - disconnectHeadset(); + mDeviceBroker.postDisconnectHeadset(); break; case BluetoothProfile.HEARING_AID: - mDeviceBroker.handleDisconnectHearingAid(); + mDeviceBroker.postDisconnectHearingAid(); break; default: @@ -579,11 +599,6 @@ public class BtHelper { } }; - private void disconnectHeadset() { - setBtScoActiveDevice(null); - mBluetoothHeadset = null; - } - //---------------------------------------------------------------------- private class ScoClient implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index b50b800f0e20..bca84f7b7217 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -391,10 +391,11 @@ public class BiometricService extends SystemService { private final Random mRandom = new Random(); // TODO(b/123378871): Remove when moved. - // When BiometricPrompt#setEnableFallback 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. + // 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; // The current authentication session, null if idle/done. We need to track both the current @@ -803,11 +804,21 @@ public class BiometricService extends SystemService { // we can't get activity results. Store the receiver somewhere so we can forward the // result back to the client. // TODO(b/123378871): Remove when moved. - if (bundle.getBoolean(BiometricPrompt.KEY_ENABLE_FALLBACK)) { + if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) { mHandler.post(() -> { - mConfirmDeviceCredentialReceiver = receiver; final KeyguardManager kgm = getContext().getSystemService( KeyguardManager.class); + if (!kgm.isDeviceSecure()) { + try { + receiver.onError(BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, + getContext().getString( + R.string.biometric_error_device_not_secured)); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + return; + } + mConfirmDeviceCredentialReceiver = receiver; // Use this so we don't need to duplicate logic.. final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */, null /* description */); diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index b65535af52b1..9e0f2fcaa29f 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -624,7 +624,8 @@ public abstract class BiometricServiceBase extends SystemService handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /*vendorCode */); - StatsLog.write(StatsLog.BIOMETRIC_HAL_DEATH_REPORTED, statsModality()); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(), + BiometricsProtoEnums.ISSUE_HAL_DEATH); } protected ClientMonitor getCurrentClient() { diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java index 91c924de27a2..3b75b95fb0b9 100644 --- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java +++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java @@ -28,7 +28,7 @@ import android.util.StatsLog; public abstract class LoggableMonitor { public static final String TAG = "BiometricStats"; - public static final boolean DEBUG = true; + public static final boolean DEBUG = false; private long mFirstAcquireTimeMs; @@ -137,6 +137,8 @@ public abstract class LoggableMonitor { + ", RequireConfirmation: " + requireConfirmation + ", State: " + authState + ", Latency: " + latency); + } else { + Slog.v(TAG, "Authentication latency: " + latency); } StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED, diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 90342ee31c3e..8995068ef504 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -168,6 +168,7 @@ public class FaceService extends BiometricServiceBase { String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) { checkPermission(USE_BIOMETRIC_INTERNAL); + updateActiveGroup(groupId, opPackageName); final boolean restricted = true; // BiometricPrompt is always restricted final AuthenticationClientImpl client = new FaceAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, @@ -704,6 +705,8 @@ public class FaceService extends BiometricServiceBase { public void serviceDied(long cookie) { super.serviceDied(cookie); mDaemon = null; + + mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate } @Override diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 62947c7dd8b4..d8544e324618 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -49,6 +49,7 @@ import android.os.SELinux; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; +import android.util.StatsLog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -192,6 +193,7 @@ public class FingerprintService extends BiometricServiceBase { IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) { checkPermission(MANAGE_BIOMETRIC); + updateActiveGroup(groupId, opPackageName); final boolean restricted = true; // BiometricPrompt is always restricted final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, @@ -553,6 +555,8 @@ public class FingerprintService extends BiometricServiceBase { + " " + f.getDeviceId()); FingerprintUtils.getInstance().removeBiometricForUser(getContext(), getTargetUserId(), f.getBiometricId()); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK); } mEnrolledList.clear(); } @@ -1002,6 +1006,8 @@ public class FingerprintService extends BiometricServiceBase { mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(), uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName()); removeInternal(client); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(), + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); } else { clearEnumerateState(); } diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java index d872e4d428ab..6cff57d4bbb1 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java @@ -17,6 +17,8 @@ package com.android.server.connectivity; import static android.net.NattSocketKeepalive.NATT_PORT; +import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER; +import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER; import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE; import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE; import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE; @@ -37,6 +39,9 @@ import android.net.NattKeepalivePacketData; import android.net.NetworkAgent; import android.net.NetworkUtils; import android.net.SocketKeepalive.InvalidPacketException; +import android.net.SocketKeepalive.InvalidSocketException; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketData.TcpSocketInfo; import android.net.util.IpUtils; import android.os.Binder; import android.os.Handler; @@ -65,7 +70,7 @@ import java.util.HashMap; * * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its - * methods must be called only from the ConnectivityService handler thread. + * handle* methods must be called only from the ConnectivityService handler thread. */ public class KeepaliveTracker { @@ -78,9 +83,12 @@ public class KeepaliveTracker { private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = new HashMap<> (); private final Handler mConnectivityServiceHandler; + @NonNull + private final TcpKeepaliveController mTcpController; public KeepaliveTracker(Handler handler) { mConnectivityServiceHandler = handler; + mTcpController = new TcpKeepaliveController(handler); } /** @@ -96,20 +104,33 @@ public class KeepaliveTracker { private final int mUid; private final int mPid; private final NetworkAgentInfo mNai; + private final int mType; + private final FileDescriptor mFd; - /** Keepalive slot. A small integer that identifies this keepalive among the ones handled - * by this network. */ + public static final int TYPE_NATT = 1; + public static final int TYPE_TCP = 2; + + // Keepalive slot. A small integer that identifies this keepalive among the ones handled + // by this network. private int mSlot = NO_KEEPALIVE; // Packet data. private final KeepalivePacketData mPacket; private final int mInterval; - // Whether the keepalive is started or not. - public boolean isStarted; - - public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai, - KeepalivePacketData packet, int interval) { + // Whether the keepalive is started or not. The initial state is NOT_STARTED. + private static final int NOT_STARTED = 1; + private static final int STARTING = 2; + private static final int STARTED = 3; + private int mStartedState = NOT_STARTED; + + KeepaliveInfo(@NonNull Messenger messenger, + @NonNull IBinder binder, + @NonNull NetworkAgentInfo nai, + @NonNull KeepalivePacketData packet, + int interval, + int type, + @NonNull FileDescriptor fd) { mMessenger = messenger; mBinder = binder; mPid = Binder.getCallingPid(); @@ -118,6 +139,8 @@ public class KeepaliveTracker { mNai = nai; mPacket = packet; mInterval = interval; + mType = type; + mFd = fd; try { mBinder.linkToDeath(this, 0); @@ -130,32 +153,40 @@ public class KeepaliveTracker { return mNai; } + private String startedStateString(final int state) { + switch (state) { + case NOT_STARTED : return "NOT_STARTED"; + case STARTING : return "STARTING"; + case STARTED : return "STARTED"; + } + throw new IllegalArgumentException("Unknown state"); + } + public String toString() { - return new StringBuffer("KeepaliveInfo [") - .append(" network=").append(mNai.network) - .append(" isStarted=").append(isStarted) - .append(" ") - .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)) - .append("->") - .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)) - .append(" interval=" + mInterval) - .append(" packetData=" + HexDump.toHexString(mPacket.getPacket())) - .append(" uid=").append(mUid).append(" pid=").append(mPid) - .append(" ]") - .toString(); + return "KeepaliveInfo [" + + " network=" + mNai.network + + " startedState=" + startedStateString(mStartedState) + + " " + + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort) + + "->" + + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort) + + " interval=" + mInterval + + " uid=" + mUid + " pid=" + mPid + + " packetData=" + HexDump.toHexString(mPacket.getPacket()) + + " ]"; } /** Sends a message back to the application via its SocketKeepalive.Callback. */ void notifyMessenger(int slot, int err) { + if (DBG) { + Log.d(TAG, "notify keepalive " + mSlot + " on " + mNai.network + " for " + err); + } KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err); } /** Called when the application process is killed. */ public void binderDied() { - // Not called from ConnectivityService handler thread, so send it a message. - mConnectivityServiceHandler.obtainMessage( - NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, - mSlot, BINDER_DIED, mNai.network).sendToTarget(); + stop(BINDER_DIED); } void unlinkDeathRecipient() { @@ -202,7 +233,26 @@ public class KeepaliveTracker { int error = isValid(); if (error == SUCCESS) { Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name()); - mNai.asyncChannel.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); + switch (mType) { + case TYPE_NATT: + mNai.asyncChannel + .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); + break; + case TYPE_TCP: + mTcpController.startSocketMonitor(mFd, this, mSlot); + mNai.asyncChannel + .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, + mPacket); + // TODO: check result from apf and notify of failure as needed. + mNai.asyncChannel + .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); + break; + default: + Log.wtf(TAG, "Starting keepalive with unknown type: " + mType); + handleStopKeepalive(mNai, mSlot, error); + return; + } + mStartedState = STARTING; } else { handleStopKeepalive(mNai, mSlot, error); return; @@ -216,15 +266,27 @@ public class KeepaliveTracker { Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); } } - if (isStarted) { + if (NOT_STARTED != mStartedState) { Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name()); - mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); + if (mType == TYPE_NATT) { + mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); + } else if (mType == TYPE_TCP) { + mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); + mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot); + mTcpController.stopSocketMonitor(mSlot); + } else { + Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType); + } } // TODO: at the moment we unconditionally return failure here. In cases where the // NetworkAgent is alive, should we ask it to reply, so it can return failure? notifyMessenger(mSlot, reason); unlinkDeathRecipient(); } + + void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) { + handleStopKeepalive(mNai, mSlot, socketKeepaliveReason); + } } void notifyMessenger(Messenger messenger, int slot, int err) { @@ -328,20 +390,38 @@ public class KeepaliveTracker { return; } - if (reason == SUCCESS && !ki.isStarted) { + // This can be called in a number of situations : + // - startedState is STARTING. + // - reason is SUCCESS => go to STARTED. + // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive. + // - startedState is STARTED. + // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive. + // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive. + // The control is not supposed to ever come here if the state is NOT_STARTED. This is + // because in NOT_STARTED state, the code will switch to STARTING before sending messages + // to start, and the only way to NOT_STARTED is this function, through the edges outlined + // above : in all cases, keepalive gets stopped and can't restart without going into + // STARTING as messages are ordered. This also depends on the hardware processing the + // messages in order. + // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an + // option. + if (reason == SUCCESS && KeepaliveInfo.STARTING == ki.mStartedState) { // Keepalive successfully started. if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name()); - ki.isStarted = true; + ki.mStartedState = KeepaliveInfo.STARTED; ki.notifyMessenger(slot, reason); } else { // Keepalive successfully stopped, or error. - ki.isStarted = false; + ki.mStartedState = KeepaliveInfo.NOT_STARTED; if (reason == SUCCESS) { + // The message indicated success stopping : don't call handleStopKeepalive. if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name()); } else { + // The message indicated some error trying to start or during the course of + // keepalive : do call handleStopKeepalive. + handleStopKeepalive(nai, slot, reason); if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason); } - handleStopKeepalive(nai, slot, reason); } } @@ -379,7 +459,47 @@ public class KeepaliveTracker { notifyMessenger(messenger, NO_KEEPALIVE, e.error); return; } - KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds); + KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds, + KeepaliveInfo.TYPE_NATT, null); + mConnectivityServiceHandler.obtainMessage( + NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); + } + + /** + * Called by ConnectivityService to start TCP keepalive on a file descriptor. + * + * In order to offload keepalive for application correctly, sequence number, ack number and + * other fields are needed to form the keepalive packet. Thus, this function synchronously + * puts the socket into repair mode to get the necessary information. After the socket has been + * put into repair mode, the application cannot access the socket until reverted to normal. + * + * See {@link android.net.SocketKeepalive}. + **/ + public void startTcpKeepalive(@Nullable NetworkAgentInfo nai, + @NonNull FileDescriptor fd, + int intervalSeconds, + @NonNull Messenger messenger, + @NonNull IBinder binder) { + if (nai == null) { + notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK); + return; + } + + TcpKeepalivePacketData packet = null; + try { + TcpSocketInfo tsi = TcpKeepaliveController.switchToRepairMode(fd); + packet = TcpKeepalivePacketData.tcpKeepalivePacket(tsi); + } catch (InvalidPacketException | InvalidSocketException e) { + try { + TcpKeepaliveController.switchOutOfRepairMode(fd); + } catch (ErrnoException e1) { + Log.e(TAG, "Couldn't move fd out of repair mode after failure to start keepalive"); + } + notifyMessenger(messenger, NO_KEEPALIVE, e.error); + return; + } + KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds, + KeepaliveInfo.TYPE_TCP, fd); Log.d(TAG, "Created keepalive: " + ki.toString()); mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); } diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java index 640504ff6e29..8a9ac23cf06a 100644 --- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java +++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java @@ -15,7 +15,6 @@ */ package com.android.server.connectivity; -import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE; import static android.net.SocketKeepalive.DATA_RECEIVED; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE; @@ -31,10 +30,8 @@ import android.net.SocketKeepalive.InvalidSocketException; import android.net.TcpKeepalivePacketData.TcpSocketInfo; import android.net.TcpRepairWindow; import android.os.Handler; -import android.os.Message; import android.os.MessageQueue; import android.os.Messenger; -import android.os.RemoteException; import android.system.ErrnoException; import android.system.Int32Ref; import android.system.Os; @@ -42,6 +39,7 @@ import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo; import java.io.FileDescriptor; import java.net.InetAddress; @@ -111,7 +109,7 @@ public class TcpKeepaliveController { * tcp/ip information. */ // TODO : make this private. It's far too confusing that this gets called from outside - // at a time that nobody can understand, but the switch out is in this class only. + // at a time that nobody can understand. public static TcpSocketInfo switchToRepairMode(FileDescriptor fd) throws InvalidSocketException { if (DBG) Log.i(TAG, "switchToRepairMode to start tcp keepalive : " + fd); @@ -199,7 +197,13 @@ public class TcpKeepaliveController { trw.rcvWndScale); } - private static void switchOutOfRepairMode(@NonNull final FileDescriptor fd) + /** + * Switch the tcp socket out of repair mode. + * + * @param fd the fd of socket to switch back to normal. + */ + // TODO : make this private. + public static void switchOutOfRepairMode(@NonNull final FileDescriptor fd) throws ErrnoException { Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF); } @@ -212,7 +216,7 @@ public class TcpKeepaliveController { * @param slot keepalive slot. */ public void startSocketMonitor(@NonNull final FileDescriptor fd, - @NonNull final Messenger messenger, final int slot) { + @NonNull final KeepaliveInfo ki, final int slot) { synchronized (mListeners) { if (null != mListeners.get(slot)) { throw new IllegalArgumentException("This slot is already taken"); @@ -226,31 +230,13 @@ public class TcpKeepaliveController { // This can't be called twice because the queue guarantees that once the listener // is unregistered it can't be called again, even for a message that arrived // before it was unregistered. - int result; - try { - // First move the socket out of repair mode. - if (DBG) Log.d(TAG, "Moving socket out of repair mode for event : " + readyFd); - switchOutOfRepairMode(readyFd); - result = (0 != (events & EVENT_ERROR)) ? ERROR_INVALID_SOCKET : DATA_RECEIVED; - } catch (ErrnoException e) { - // Could not move the socket out of repair mode. Still continue with notifying - // the client - Log.e(TAG, "Cannot switch socket out of repair mode", e); - result = ERROR_INVALID_SOCKET; - } - // Prepare and send the message to the receiver. - final Message message = Message.obtain(); - message.what = EVENT_SOCKET_KEEPALIVE; - message.arg1 = slot; - message.arg2 = result; - try { - messenger.send(message); - } catch (RemoteException e) { - // Remote process died - } - synchronized (mListeners) { - mListeners.remove(slot); + final int reason; + if (0 != (events & EVENT_ERROR)) { + reason = ERROR_INVALID_SOCKET; + } else { + reason = DATA_RECEIVED; } + ki.onFileDescriptorInitiatedStop(reason); // The listener returns the new set of events to listen to. Because 0 means no // event, the listener gets unregistered. return 0; diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index e268e4458986..bfa7f9d439e3 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -899,9 +899,20 @@ public final class ContentService extends IContentService.Stub { @Override public void setIsSyncable(Account account, String providerName, int syncable) { + setIsSyncableAsUser(account, providerName, syncable, UserHandle.getCallingUserId()); + } + + /** + * @hide + */ + @Override + public void setIsSyncableAsUser(Account account, String providerName, int syncable, + int userId) { if (TextUtils.isEmpty(providerName)) { throw new IllegalArgumentException("Authority must not be empty"); } + enforceCrossUserPermission(userId, + "no permission to set the sync settings for user " + userId); mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); @@ -909,7 +920,6 @@ public final class ContentService extends IContentService.Stub { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 7096477ce5f4..99e07071f361 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -3261,7 +3261,7 @@ public class SyncManager { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Account " + aau.account + " added, checking sync restore data"); } - AccountSyncSettingsBackupHelper.accountAdded(mContext); + AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId); break; } } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 727cf0e9f084..126beeffbb96 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -34,6 +34,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; +import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayedContentSample; @@ -57,7 +58,6 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.ColorDisplayController; import com.android.internal.os.BackgroundThread; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.RingBuffer; @@ -382,9 +382,8 @@ public class BrightnessTracker { return; } - builder.setNightMode(mInjector.isNightModeActive(mContext, UserHandle.USER_CURRENT)); - builder.setColorTemperature(mInjector.getColorTemperature(mContext, - UserHandle.USER_CURRENT)); + builder.setNightMode(mInjector.isNightDisplayActivated(mContext)); + builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext)); if (mColorSamplingEnabled) { DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample); @@ -1096,12 +1095,13 @@ public class BrightnessTracker { return context.getSystemService(PowerManager.class).isInteractive(); } - public int getColorTemperature(Context context, int userId) { - return new ColorDisplayController(context, userId).getColorTemperature(); + public int getNightDisplayColorTemperature(Context context) { + return context.getSystemService(ColorDisplayManager.class) + .getNightDisplayColorTemperature(); } - public boolean isNightModeActive(Context context, int userId) { - return new ColorDisplayController(context, userId).isActivated(); + public boolean isNightDisplayActivated(Context context) { + return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated(); } public DisplayedContentSample sampleColor(int noFramesToSample) { diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index b3a1a06eedc2..9cb6eeec2126 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -72,7 +72,6 @@ import android.view.animation.AnimationUtils; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.ColorDisplayController; import com.android.internal.util.DumpUtils; import com.android.server.DisplayThread; import com.android.server.SystemService; @@ -115,6 +114,7 @@ public final class ColorDisplayService extends SystemService { private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0; private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1; private static final int MSG_APPLY_GLOBAL_SATURATION = 2; + private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 3; /** * Return value if a setting has not been set. @@ -323,8 +323,7 @@ public final class ColorDisplayService extends SystemService { } private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() { - IBinder displayToken = SurfaceControl.getBuiltInDisplay( - SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); + final IBinder displayToken = SurfaceControl.getInternalDisplayToken(); if (displayToken == null) { return null; } @@ -448,7 +447,6 @@ public final class ColorDisplayService extends SystemService { private ContentObserver mUserSetupObserver; private boolean mBootCompleted; - private ColorDisplayController mNightDisplayController; private ContentObserver mContentObserver; private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener; @@ -547,8 +545,6 @@ public final class ColorDisplayService extends SystemService { private void setUp() { Slog.d(TAG, "setUp: currentUser=" + mCurrentUser); - mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser); - // Listen for external changes to any of the settings. if (mContentObserver == null) { mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) { @@ -586,7 +582,7 @@ public final class ColorDisplayService extends SystemService { getNightDisplayCustomEndTimeInternal().getLocalTime()); break; case System.DISPLAY_COLOR_MODE: - onDisplayColorModeChanged(mNightDisplayController.getColorMode()); + onDisplayColorModeChanged(getColorModeInternal()); break; case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED: onAccessibilityInversionChanged(); @@ -634,7 +630,7 @@ public final class ColorDisplayService extends SystemService { // Set the color mode, if valid, and immediately apply the updated tint matrix based on the // existing activated state. This ensures consistency of tint across the color mode change. - onDisplayColorModeChanged(mNightDisplayController.getColorMode()); + onDisplayColorModeChanged(getColorModeInternal()); if (mNightDisplayTintController.isAvailable(getContext())) { // Reset the activated state. @@ -667,10 +663,6 @@ public final class ColorDisplayService extends SystemService { getContext().getContentResolver().unregisterContentObserver(mContentObserver); - if (mNightDisplayController != null) { - mNightDisplayController = null; - } - if (mNightDisplayTintController.isAvailable(getContext())) { if (mNightDisplayAutoMode != null) { mNightDisplayAutoMode.onStop(); @@ -740,7 +732,7 @@ public final class ColorDisplayService extends SystemService { } private void onAccessibilityActivated() { - onDisplayColorModeChanged(mNightDisplayController.getColorMode()); + onDisplayColorModeChanged(getColorModeInternal()); } /** @@ -871,7 +863,7 @@ public final class ColorDisplayService extends SystemService { // If disabled, clear the tint. If enabled, do nothing more here and let the next // temperature update set the correct tint. if (!activated) { - applyTint(mDisplayWhiteBalanceTintController, false); + mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE); } } @@ -1003,8 +995,7 @@ public final class ColorDisplayService extends SystemService { mCurrentUser); } - private @ColorMode - int getColorModeInternal() { + private @ColorMode int getColorModeInternal() { final ContentResolver cr = getContext().getContentResolver(); if (Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) == 1 @@ -1543,7 +1534,7 @@ public final class ColorDisplayService extends SystemService { mDisplayWhiteBalanceTintController.setMatrix(cct); if (mDisplayWhiteBalanceTintController.isActivated()) { - applyTint(mDisplayWhiteBalanceTintController, false); + mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE); return true; } return false; @@ -1603,6 +1594,9 @@ public final class ColorDisplayService extends SystemService { case MSG_APPLY_NIGHT_DISPLAY_ANIMATED: applyTint(mNightDisplayTintController, false); break; + case MSG_APPLY_DISPLAY_WHITE_BALANCE: + applyTint(mDisplayWhiteBalanceTintController, false); + break; } } } diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index f2c539cb257c..31b497d001eb 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -27,6 +27,7 @@ import android.opengl.EGLDisplay; import android.opengl.EGLSurface; import android.opengl.GLES11Ext; import android.opengl.GLES20; +import android.os.IBinder; import android.util.Slog; import android.view.DisplayInfo; import android.view.Surface; @@ -474,8 +475,14 @@ final class ColorFade { final SurfaceTexture st = new SurfaceTexture(mTexNames[0]); final Surface s = new Surface(st); try { - SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay( - SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), s); + final IBinder token = SurfaceControl.getInternalDisplayToken(); + if (token == null) { + Slog.e(TAG, + "Failed to take screenshot because internal display is disconnected"); + return false; + } + + SurfaceControl.screenshot(token, s); st.updateTexImage(); st.getTransformMatrix(mTexMatrix); } finally { @@ -629,7 +636,7 @@ final class ColorFade { mSurfaceLayout = null; SurfaceControl.openTransaction(); try { - mSurfaceControl.destroy(); + mSurfaceControl.remove(); mSurface.release(); } finally { SurfaceControl.closeTransaction(); diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java index b1b7d3c8769b..ef92401bf3fd 100644 --- a/services/core/java/com/android/server/display/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/DisplayTransformManager.java @@ -87,7 +87,7 @@ public class DisplayTransformManager { * Map of level -> color transformation matrix. */ @GuardedBy("mColorMatrix") - private final SparseArray<float[]> mColorMatrix = new SparseArray<>(3); + private final SparseArray<float[]> mColorMatrix = new SparseArray<>(5); /** * Temporary matrix used internally by {@link #computeColorMatrixLocked()}. */ @@ -148,6 +148,21 @@ public class DisplayTransformManager { } /** + * Sets the current Daltonization mode. This adjusts the color space to correct for or simulate + * various types of color blindness. + * + * @param mode the new Daltonization mode, or -1 to disable + */ + public void setDaltonizerMode(int mode) { + synchronized (mDaltonizerModeLock) { + if (mDaltonizerMode != mode) { + mDaltonizerMode = mode; + applyDaltonizerMode(mode); + } + } + } + + /** * Returns the composition of all current color matrices, or {@code null} if there are none. */ @GuardedBy("mColorMatrix") @@ -167,30 +182,6 @@ public class DisplayTransformManager { } /** - * Returns the current Daltonization mode. - */ - public int getDaltonizerMode() { - synchronized (mDaltonizerModeLock) { - return mDaltonizerMode; - } - } - - /** - * Sets the current Daltonization mode. This adjusts the color space to correct for or simulate - * various types of color blindness. - * - * @param mode the new Daltonization mode, or -1 to disable - */ - public void setDaltonizerMode(int mode) { - synchronized (mDaltonizerModeLock) { - if (mDaltonizerMode != mode) { - mDaltonizerMode = mode; - applyDaltonizerMode(mode); - } - } - } - - /** * Propagates the provided color transformation matrix to the SurfaceFlinger. */ private static void applyColorMatrix(float[] m) { diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 16d82df4dd5b..28f21f633ac4 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -17,12 +17,8 @@ package com.android.server.display; import android.app.ActivityThread; -import android.content.res.Resources; -import com.android.server.LocalServices; -import com.android.server.lights.Light; -import com.android.server.lights.LightsManager; - import android.content.Context; +import android.content.res.Resources; import android.hardware.sidekick.SidekickInternal; import android.os.Build; import android.os.Handler; @@ -31,6 +27,7 @@ import android.os.Looper; import android.os.PowerManager; import android.os.SystemProperties; import android.os.Trace; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -38,6 +35,11 @@ import android.view.DisplayCutout; import android.view.DisplayEventReceiver; import android.view.Surface; import android.view.SurfaceControl; + +import com.android.server.LocalServices; +import com.android.server.lights.Light; +import com.android.server.lights.LightsManager; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -58,13 +60,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; - private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] { - SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN, - SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI, - }; + private final LongSparseArray<LocalDisplayDevice> mDevices = + new LongSparseArray<LocalDisplayDevice>(); - private final SparseArray<LocalDisplayDevice> mDevices = - new SparseArray<LocalDisplayDevice>(); @SuppressWarnings("unused") // Becomes active at instantiation time. private HotplugDisplayEventReceiver mHotplugReceiver; @@ -80,28 +78,26 @@ final class LocalDisplayAdapter extends DisplayAdapter { mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); - for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) { - tryConnectDisplayLocked(builtInDisplayId); + for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) { + tryConnectDisplayLocked(physicalDisplayId); } } - private void tryConnectDisplayLocked(int builtInDisplayId) { - IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId); + private void tryConnectDisplayLocked(long physicalDisplayId) { + final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId); if (displayToken != null) { SurfaceControl.PhysicalDisplayInfo[] configs = SurfaceControl.getDisplayConfigs(displayToken); if (configs == null) { // There are no valid configs for this device, so we can't use it - Slog.w(TAG, "No valid configs found for display device " + - builtInDisplayId); + Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId); return; } int activeConfig = SurfaceControl.getActiveConfig(displayToken); if (activeConfig < 0) { // There is no active config, and for now we don't have the // policy to set one. - Slog.w(TAG, "No active config found for display device " + - builtInDisplayId); + Slog.w(TAG, "No active config found for display device " + physicalDisplayId); return; } int activeColorMode = SurfaceControl.getActiveColorMode(displayToken); @@ -110,16 +106,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { // configuration pass we'll go ahead and set it to whatever it was set to last (or // COLOR_MODE_NATIVE if this is the first configuration). Slog.w(TAG, "Unable to get active color mode for display device " + - builtInDisplayId); + physicalDisplayId); activeColorMode = Display.COLOR_MODE_INVALID; } int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken); - LocalDisplayDevice device = mDevices.get(builtInDisplayId); + LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { // Display was added. - device = new LocalDisplayDevice(displayToken, builtInDisplayId, - configs, activeConfig, colorModes, activeColorMode); - mDevices.put(builtInDisplayId, device); + final boolean isInternal = mDevices.size() == 0; + device = new LocalDisplayDevice(displayToken, physicalDisplayId, + configs, activeConfig, colorModes, activeColorMode, isInternal); + mDevices.put(physicalDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig, colorModes, activeColorMode)) { @@ -133,11 +130,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - private void tryDisconnectDisplayLocked(int builtInDisplayId) { - LocalDisplayDevice device = mDevices.get(builtInDisplayId); + private void tryDisconnectDisplayLocked(long physicalDisplayId) { + LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device != null) { // Display was removed. - mDevices.remove(builtInDisplayId); + mDevices.remove(physicalDisplayId); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); } } @@ -158,10 +155,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { } private final class LocalDisplayDevice extends DisplayDevice { - private final int mBuiltInDisplayId; + private final long mPhysicalDisplayId; private final Light mBacklight; private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>(); private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>(); + private final boolean mIsInternal; private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; @@ -179,16 +177,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[]; - public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, + LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo, - int[] colorModes, int activeColorMode) { - super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId); - mBuiltInDisplayId = builtInDisplayId; + int[] colorModes, int activeColorMode, boolean isInternal) { + super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId); + mPhysicalDisplayId = physicalDisplayId; + mIsInternal = isInternal; updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo, colorModes, activeColorMode); updateColorModesLocked(colorModes, activeColorMode); mSidekickInternal = LocalServices.getService(SidekickInternal.class); - if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { + if (mIsInternal) { LightsManager lights = LocalServices.getService(LightsManager.class); mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT); } else { @@ -392,7 +391,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } final Resources res = getOverlayContext().getResources(); - if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { + if (mIsInternal) { mInfo.name = res.getString( com.android.internal.R.string.display_manager_built_in_display_name); mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY @@ -455,7 +454,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { final boolean stateChanged = (mState != state); final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null; if (stateChanged || brightnessChanged) { - final int displayId = mBuiltInDisplayId; + final long physicalDisplayId = mPhysicalDisplayId; final IBinder token = getDisplayTokenLocked(); final int oldState = mState; @@ -519,7 +518,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void setVrMode(boolean isVrEnabled) { if (DEBUG) { Slog.d(TAG, "setVrMode(" - + "id=" + displayId + + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); } mBacklight.setVrMode(isVrEnabled); @@ -528,7 +527,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void setDisplayState(int state) { if (DEBUG) { Slog.d(TAG, "setDisplayState(" - + "id=" + displayId + + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); } @@ -546,7 +545,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } final int mode = getPowerModeForState(state); Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState(" - + "id=" + displayId + + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); try { SurfaceControl.setDisplayPowerMode(token, mode); @@ -571,11 +570,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void setDisplayBrightness(int brightness) { if (DEBUG) { Slog.d(TAG, "setDisplayBrightness(" - + "id=" + displayId + ", brightness=" + brightness + ")"); + + "id=" + physicalDisplayId + + ", brightness=" + brightness + ")"); } Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" - + "id=" + displayId + ", brightness=" + brightness + ")"); + + "id=" + physicalDisplayId + ", brightness=" + brightness + ")"); try { mBacklight.setBrightness(brightness); Trace.traceCounter(Trace.TRACE_TAG_POWER, @@ -646,7 +646,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public void dumpLocked(PrintWriter pw) { super.dumpLocked(pw); - pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); + pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveColorMode=" + mActiveColorMode); @@ -731,12 +731,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { + public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { synchronized (getSyncRoot()) { if (connected) { - tryConnectDisplayLocked(builtInDisplayId); + tryConnectDisplayLocked(physicalDisplayId); } else { - tryDisconnectDisplayLocked(builtInDisplayId); + tryDisconnectDisplayLocked(physicalDisplayId); } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 7376ed2b0679..072238ee7f55 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -2412,7 +2412,8 @@ public class HdmiControlService extends SystemService { void wakeUp() { assertRunOnServiceThread(); mWakeUpMessageReceived = true; - mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE"); + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_HDMI, + "android.server.hdmi:WAKE"); // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets // the intent, the sequence will continue at onWakeUp(). } @@ -2637,7 +2638,8 @@ public class HdmiControlService extends SystemService { playback().sendStandby(0 /* unused */); } } else if (isPowerStandbyOrTransient() && !isStandbyModeOn) { - mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE"); + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_HDMI, + "android.server.hdmi:WAKE"); if (playback() != null) { oneTouchPlay(new IHdmiControlCallback.Stub() { @Override diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 532aa01f4438..2bfb31f6e74f 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -286,6 +286,46 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } /** + * Sets whether the default service should be used. + * + * <p>Typically used during CTS tests to make sure only the default service doesn't interfere + * with the test results. + * + * @throws SecurityException if caller is not allowed to manage this service's settings. + */ + public final void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { + Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled); + enforceCallingPermissionForManagement(); + + synchronized (mLock) { + final S oldService = peekServiceForUserLocked(userId); + if (oldService != null) { + oldService.removeSelfFromCacheLocked(); + } + mServiceNameResolver.setDefaultServiceEnabled(userId, enabled); + + // Must update the service on cache so its initialization code is triggered + updateCachedServiceLocked(userId); + } + } + + /** + * Checks whether the default service should be used. + * + * <p>Typically used during CTS tests to make sure only the default service doesn't interfere + * with the test results. + * + * @throws SecurityException if caller is not allowed to manage this service's settings. + */ + public final boolean isDefaultServiceEnabled(@UserIdInt int userId) { + enforceCallingPermissionForManagement(); + + synchronized (mLock) { + return mServiceNameResolver.isDefaultServiceEnabled(userId); + } + } + + /** * Gets the maximum time the service implementation can be changed. * * @throws UnsupportedOperationException if subclass doesn't override it. diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java index 7f198acfd743..cf84e22e7dd1 100644 --- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java +++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java @@ -27,6 +27,7 @@ import android.os.SystemClock; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; @@ -61,6 +62,15 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>(); /** + * Map of default services that have been disabled by + * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}. + * + * <p>Typically used by Shell command and/or CTS tests. + */ + @GuardedBy("mLock") + private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray(); + + /** * When the temporary service will expire (and reset back to the default). */ @GuardedBy("mLock") @@ -99,12 +109,18 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR final String temporaryName = mTemporaryServiceNames.get(userId); if (temporaryName != null) { // Always log it, as it should only be used on CTS or during development - Slog.w(TAG, "getComponentName(): using temporary name " + temporaryName + Slog.w(TAG, "getServiceName(): using temporary name " + temporaryName + " for user " + userId); return temporaryName; - } else { - return getDefaultServiceName(userId); } + final boolean disabled = mDefaultServicesDisabled.get(userId); + if (disabled) { + // Always log it, as it should only be used on CTS or during development + Slog.w(TAG, "getServiceName(): temporary name not set and default disabled for " + + "user " + userId); + return null; + } + return getDefaultServiceName(userId); } } @@ -158,6 +174,24 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR } @Override + public void setDefaultServiceEnabled(int userId, boolean enabled) { + synchronized (mLock) { + if (enabled) { + mDefaultServicesDisabled.removeAt(userId); + } else { + mDefaultServicesDisabled.put(userId, true); + } + } + } + + @Override + public boolean isDefaultServiceEnabled(int userId) { + synchronized (mLock) { + return mDefaultServicesDisabled.get(userId); + } + } + + @Override public String toString() { return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]"; } @@ -168,6 +202,7 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR synchronized (mLock) { pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId); pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size()); + pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size()); } } @@ -181,7 +216,9 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime(); pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), "); } - pw.print("defaultName="); pw.println(getDefaultServiceName(userId)); + pw.print("defaultName="); pw.print(getDefaultServiceName(userId)); + final boolean disabled = mDefaultServicesDisabled.get(userId); + pw.println(disabled ? " (disabled)" : " (enabled)"); } } diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java index bc11ff302ca9..5b60413eefad 100644 --- a/services/core/java/com/android/server/infra/ServiceNameResolver.java +++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java @@ -108,6 +108,36 @@ public interface ServiceNameResolver { } /** + * Sets whether the default service should be used when the temporary service is not set. + * + * <p>Typically used during CTS tests to make sure only the default service doesn't interfere + * with the test results. + * + * @param userId user handle + * @param enabled whether the default service should be used when the temporary service is not + * set + * + * @throws UnsupportedOperationException if not implemented. + */ + default void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { + throw new UnsupportedOperationException("changing default service not supported"); + } + + /** + * Checks whether the default service should be used when the temporary service is not set. + * + * <p>Typically used during CTS tests to make sure only the default service doesn't interfere + * with the test results. + * + * @param userId user handle + * + * @throws UnsupportedOperationException if not implemented. + */ + default boolean isDefaultServiceEnabled(@UserIdInt int userId) { + throw new UnsupportedOperationException("checking default service not supported"); + } + + /** * Dumps the generic info in just one line (without calling {@code println}. */ // TODO(b/117779333): support proto diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 19d10ecfb34d..cefe583fc5d1 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -509,7 +509,7 @@ public class JobSchedulerService extends com.android.server.SystemService private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_USE_HEARTBEATS = false; - private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; + private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 10 * 60 * 1000L; // 10 minutes private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index 97c3bac4ddbb..74628fb9b502 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -110,6 +110,7 @@ public abstract class StateController { final boolean jobWouldBeReady = jobStatus.wouldBeReadyWithConstraint(constraint); if (DEBUG) { Slog.v(TAG, "wouldBeReadyWithConstraintLocked: " + jobStatus.toShortString() + + " constraint=" + constraint + " readyWithConstraint=" + jobWouldBeReady); } if (!jobWouldBeReady) { diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index 26f3caf2e487..70deb38080b4 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -149,8 +149,8 @@ public final class TimeController extends StateController { @Override public void onConstantsUpdatedLocked() { - checkExpiredDelaysAndResetAlarm(); checkExpiredDeadlinesAndResetAlarm(); + checkExpiredDelaysAndResetAlarm(); } @Override @@ -159,20 +159,47 @@ public final class TimeController extends StateController { return; } - if (job.hasTimingDelayConstraint() - && job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) { - checkExpiredDelaysAndResetAlarm(); - } + final long nowElapsedMillis = sElapsedRealtimeClock.millis(); + + // Check deadline constraint first because if it's satisfied, we avoid a little bit of + // unnecessary processing of the timing delay. if (job.hasDeadlineConstraint() + && !job.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE) && job.getLatestRunTimeElapsed() <= mNextJobExpiredElapsedMillis) { - checkExpiredDeadlinesAndResetAlarm(); + if (evaluateDeadlineConstraint(job, nowElapsedMillis)) { + checkExpiredDeadlinesAndResetAlarm(); + checkExpiredDelaysAndResetAlarm(); + } else { + final boolean isAlarmForJob = + job.getLatestRunTimeElapsed() == mNextJobExpiredElapsedMillis; + final boolean wouldBeReady = wouldBeReadyWithConstraintLocked( + job, JobStatus.CONSTRAINT_DEADLINE); + if ((isAlarmForJob && !wouldBeReady) || (!isAlarmForJob && wouldBeReady)) { + checkExpiredDeadlinesAndResetAlarm(); + } + } + } + if (job.hasTimingDelayConstraint() + && !job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY) + && job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) { + if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) { + checkExpiredDelaysAndResetAlarm(); + } else { + final boolean isAlarmForJob = + job.getEarliestRunTime() == mNextDelayExpiredElapsedMillis; + final boolean wouldBeReady = wouldBeReadyWithConstraintLocked( + job, JobStatus.CONSTRAINT_TIMING_DELAY); + if ((isAlarmForJob && !wouldBeReady) || (!isAlarmForJob && wouldBeReady)) { + checkExpiredDelaysAndResetAlarm(); + } + } } } @Override public void reevaluateStateLocked(int uid) { - checkExpiredDelaysAndResetAlarm(); checkExpiredDeadlinesAndResetAlarm(); + checkExpiredDelaysAndResetAlarm(); } /** @@ -182,10 +209,10 @@ public final class TimeController extends StateController { * back and forth. */ private boolean canStopTrackingJobLocked(JobStatus job) { - return (!job.hasTimingDelayConstraint() || - (job.satisfiedConstraints&JobStatus.CONSTRAINT_TIMING_DELAY) != 0) && - (!job.hasDeadlineConstraint() || - (job.satisfiedConstraints&JobStatus.CONSTRAINT_DEADLINE) != 0); + return (!job.hasTimingDelayConstraint() + || job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) + && (!job.hasDeadlineConstraint() + || job.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE)); } private void ensureAlarmServiceLocked() { @@ -241,6 +268,7 @@ public final class TimeController extends StateController { } } + /** @return true if the job's deadline constraint is satisfied */ private boolean evaluateDeadlineConstraint(JobStatus job, long nowElapsedMillis) { final long jobDeadline = job.getLatestRunTimeElapsed(); @@ -279,7 +307,7 @@ public final class TimeController extends StateController { if (job.isReady()) { ready = true; } - } else if (!job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) { + } else { if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS && !wouldBeReadyWithConstraintLocked( job, JobStatus.CONSTRAINT_TIMING_DELAY)) { @@ -319,6 +347,7 @@ public final class TimeController extends StateController { } } + /** @return true if the job's delay constraint is satisfied */ private boolean evaluateTimingDelayConstraint(JobStatus job, long nowElapsedMillis) { final long jobDelayTime = job.getEarliestRunTime(); if (jobDelayTime <= nowElapsedMillis) { @@ -347,6 +376,9 @@ public final class TimeController extends StateController { */ private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); + if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) { + return; + } mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis; updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws); @@ -359,6 +391,9 @@ public final class TimeController extends StateController { */ private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); + if (mNextJobExpiredElapsedMillis == alarmTimeElapsedMillis) { + return; + } mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis; updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener, mNextJobExpiredElapsedMillis, ws); diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java index 72259268aa81..91b5234e02e5 100644 --- a/services/core/java/com/android/server/location/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/GnssConfiguration.java @@ -367,7 +367,7 @@ class GnssConfiguration { return defaultValue; } try { - return Integer.parseInt(valueString); + return Integer.decode(valueString); } catch (NumberFormatException e) { Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: " + valueString + ". Using default value: " + defaultValue); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index f368e7b8494e..2f0b388b9a7d 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -302,7 +302,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements MAX_RETRY_INTERVAL); // true if we are enabled, protected by this - private boolean mEnabled = true; + private boolean mEnabled; private boolean mShutdown; diff --git a/services/core/java/com/android/server/location/LocationBasedCountryDetector.java b/services/core/java/com/android/server/location/LocationBasedCountryDetector.java index 6527899f2979..8ee1285b41f2 100644 --- a/services/core/java/com/android/server/location/LocationBasedCountryDetector.java +++ b/services/core/java/com/android/server/location/LocationBasedCountryDetector.java @@ -235,18 +235,15 @@ public class LocationBasedCountryDetector extends CountryDetectorBase { * Start a new thread to query the country from Geocoder. */ private synchronized void queryCountryCode(final Location location) { - if (location == null) { - notifyListener(null); - return; - } if (mQueryThread != null) return; mQueryThread = new Thread(new Runnable() { @Override public void run() { - String countryIso = null; - if (location != null) { - countryIso = getCountryFromLocation(location); + if (location == null) { + notifyListener(null); + return; } + String countryIso = getCountryFromLocation(location); if (countryIso != null) { mDetectedCountry = new Country(countryIso, Country.COUNTRY_SOURCE_LOCATION); } else { diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 34c8786b00b3..afe34737fdee 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -183,12 +183,17 @@ public class LocationProviderProxy extends AbstractLocationProvider { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" service=" + mServiceWatcher); + pw.println(" service=" + mServiceWatcher); + synchronized (mProviderPackagesLock) { + if (mProviderPackages.size() > 1) { + pw.println(" additional packages=" + mProviderPackages); + } + } mServiceWatcher.runOnBinderBlocking(binder -> { try { TransferPipe.dumpAsync(binder, fd, args); } catch (IOException | RemoteException e) { - pw.println(" failed to dump location provider"); + pw.println(" <failed to dump location provider: " + e + ">"); } return null; }, null); diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java new file mode 100644 index 000000000000..358bdb90f6d3 --- /dev/null +++ b/services/core/java/com/android/server/notification/BubbleExtractor.java @@ -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. +*/ +package com.android.server.notification; + +import android.content.Context; +import android.util.Slog; + +/** + * Determines whether a bubble can be shown for this notification + */ +public class BubbleExtractor implements NotificationSignalExtractor { + private static final String TAG = "BubbleExtractor"; + private static final boolean DBG = false; + + private RankingConfig mConfig; + + public void initialize(Context ctx, NotificationUsageStats usageStats) { + if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + } + + public RankingReconsideration process(NotificationRecord record) { + if (record == null || record.getNotification() == null) { + if (DBG) Slog.d(TAG, "skipping empty notification"); + return null; + } + + if (mConfig == null) { + if (DBG) Slog.d(TAG, "missing config"); + return null; + } + boolean userWantsBubbles = mConfig.bubblesEnabled(record.sbn.getUser()); + boolean appCanShowBubble = + mConfig.areBubblesAllowed(record.sbn.getPackageName(), record.sbn.getUid()); + if (!userWantsBubbles || !appCanShowBubble) { + record.setAllowBubble(false); + } else { + if (record.getChannel() != null) { + record.setAllowBubble(record.getChannel().canBubble() && appCanShowBubble); + } else { + record.setAllowBubble(appCanShowBubble); + } + } + + return null; + } + + @Override + public void setConfig(RankingConfig config) { + mConfig = config; + } + + @Override + public void setZenHelper(ZenModeHelper helper) { + } +} diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index c222e6948a3d..cf09b8fc4c84 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -306,18 +306,21 @@ abstract public class ManagedServices { } } - public void writeXml(XmlSerializer out, boolean forBackup) throws IOException { + public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException { out.startTag(null, getConfig().xmlTag); out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION)); if (forBackup) { - trimApprovedListsAccordingToInstalledServices(); + trimApprovedListsAccordingToInstalledServices(userId); } final int N = mApproved.size(); for (int i = 0 ; i < N; i++) { - final int userId = mApproved.keyAt(i); + final int approvedUserId = mApproved.keyAt(i); + if (forBackup && approvedUserId != userId) { + continue; + } final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); if (approvedByType != null) { final int M = approvedByType.size(); @@ -328,14 +331,14 @@ abstract public class ManagedServices { String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved); out.startTag(null, TAG_MANAGED_SERVICES); out.attribute(null, ATT_APPROVED_LIST, allowedItems); - out.attribute(null, ATT_USER_ID, Integer.toString(userId)); + out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); out.endTag(null, TAG_MANAGED_SERVICES); if (!forBackup && isPrimary) { // Also write values to settings, for observers who haven't migrated yet Settings.Secure.putStringForUser(mContext.getContentResolver(), - getConfig().secureSettingName, allowedItems, userId); + getConfig().secureSettingName, allowedItems, approvedUserId); } } @@ -350,15 +353,12 @@ abstract public class ManagedServices { loadAllowedComponentsFromSettings(); } - public void readXml(XmlPullParser parser, Predicate<String> allowedManagedServicePackages) + public void readXml( + XmlPullParser parser, + Predicate<String> allowedManagedServicePackages, + boolean forRestore, + int userId) throws XmlPullParserException, IOException { - // upgrade xml - int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0); - final List<UserInfo> activeUsers = mUm.getUsers(true); - for (UserInfo userInfo : activeUsers) { - upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier()); - } - // read grants int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -372,14 +372,16 @@ abstract public class ManagedServices { Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml"); final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST); - final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); + // Ignore parser's user id for restore. + final int resolvedUserId = forRestore + ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); final boolean isPrimary = XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true); if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(getPackageName(approved))) { - if (mUm.getUserInfo(userId) != null) { - addApprovedList(approved, userId, isPrimary); + if (mUm.getUserInfo(resolvedUserId) != null) { + addApprovedList(approved, resolvedUserId, isPrimary); } mUseXml = true; } @@ -389,8 +391,6 @@ abstract public class ManagedServices { rebindServices(false, USER_ALL); } - protected void upgradeXml(final int xmlVersion, final int userId) {} - private void loadAllowedComponentsFromSettings() { for (UserInfo user : mUm.getUsers()) { final ContentResolver cr = mContext.getContentResolver(); @@ -784,26 +784,23 @@ abstract public class ManagedServices { return allowedPackages; } - private void trimApprovedListsAccordingToInstalledServices() { - int N = mApproved.size(); - for (int i = 0 ; i < N; i++) { - final int userId = mApproved.keyAt(i); - final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); - int M = approvedByType.size(); - for (int j = 0; j < M; j++) { - final ArraySet<String> approved = approvedByType.valueAt(j); - int P = approved.size(); - for (int k = P - 1; k >= 0; k--) { - final String approvedPackageOrComponent = approved.valueAt(k); - if (!isValidEntry(approvedPackageOrComponent, userId)){ - approved.removeAt(k); - Slog.v(TAG, "Removing " + approvedPackageOrComponent - + " from approved list; no matching services found"); - } else { - if (DEBUG) { - Slog.v(TAG, "Keeping " + approvedPackageOrComponent - + " on approved list; matching services found"); - } + private void trimApprovedListsAccordingToInstalledServices(int userId) { + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); + if (approvedByType == null) { + return; + } + for (int i = 0; i < approvedByType.size(); i++) { + final ArraySet<String> approved = approvedByType.valueAt(i); + for (int j = approved.size() - 1; j >= 0; j--) { + final String approvedPackageOrComponent = approved.valueAt(j); + if (!isValidEntry(approvedPackageOrComponent, userId)){ + approved.removeAt(j); + Slog.v(TAG, "Removing " + approvedPackageOrComponent + + " from approved list; no matching services found"); + } else { + if (DEBUG) { + Slog.v(TAG, "Keeping " + approvedPackageOrComponent + + " on approved list; matching services found"); } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 34a6663c4352..3557fcf35dbc 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -213,6 +213,7 @@ import com.android.server.lights.LightsManager; import com.android.server.notification.ManagedServices.ManagedServiceInfo; import com.android.server.notification.ManagedServices.UserProfiles; import com.android.server.pm.PackageManagerService; +import com.android.server.pm.UserManagerService; import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; @@ -538,30 +539,49 @@ public class NotificationManagerService extends SystemService { } } - void readPolicyXml(InputStream stream, boolean forRestore) + UserManagerService getUserManagerService() { + return UserManagerService.getInstance(); + } + + void readPolicyXml(InputStream stream, boolean forRestore, int userId) throws XmlPullParserException, NumberFormatException, IOException { final XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY); boolean migratedManagedServices = false; + boolean ineligibleForManagedServices = forRestore + && getUserManagerService().isManagedProfile(userId); int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) { - mZenModeHelper.readXml(parser, forRestore); + mZenModeHelper.readXml(parser, forRestore, userId); } else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){ - mPreferencesHelper.readXml(parser, forRestore); + mPreferencesHelper.readXml(parser, forRestore, userId); } if (mListeners.getConfig().xmlTag.equals(parser.getName())) { - mListeners.readXml(parser, mAllowedManagedServicePackages); + if (ineligibleForManagedServices) { + continue; + } + mListeners.readXml(parser, mAllowedManagedServicePackages, forRestore, userId); migratedManagedServices = true; } else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) { - mAssistants.readXml(parser, mAllowedManagedServicePackages); + if (ineligibleForManagedServices) { + continue; + } + mAssistants.readXml(parser, mAllowedManagedServicePackages, forRestore, userId); migratedManagedServices = true; } else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) { - mConditionProviders.readXml(parser, mAllowedManagedServicePackages); + if (ineligibleForManagedServices) { + continue; + } + mConditionProviders.readXml( + parser, mAllowedManagedServicePackages, forRestore, userId); migratedManagedServices = true; } if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) { + if (forRestore && userId != UserHandle.USER_SYSTEM) { + continue; + } mLockScreenAllowSecureNotifications = safeBoolean(parser.getAttributeValue(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE), true); @@ -584,7 +604,7 @@ public class NotificationManagerService extends SystemService { InputStream infile = null; try { infile = mPolicyFile.openRead(); - readPolicyXml(infile, false /*forRestore*/); + readPolicyXml(infile, false /*forRestore*/, UserHandle.USER_ALL); } catch (FileNotFoundException e) { // No data yet // Load default managed services approvals @@ -615,7 +635,7 @@ public class NotificationManagerService extends SystemService { } try { - writePolicyXml(stream, false /*forBackup*/); + writePolicyXml(stream, false /*forBackup*/, UserHandle.USER_ALL); mPolicyFile.finishWrite(stream); } catch (IOException e) { Slog.w(TAG, "Failed to save policy file, restoring backup", e); @@ -626,18 +646,21 @@ public class NotificationManagerService extends SystemService { }); } - private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException { + private void writePolicyXml(OutputStream stream, boolean forBackup, int userId) + throws IOException { final XmlSerializer out = new FastXmlSerializer(); out.setOutput(stream, StandardCharsets.UTF_8.name()); out.startDocument(null, true); out.startTag(null, TAG_NOTIFICATION_POLICY); out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); - mZenModeHelper.writeXml(out, forBackup, null); - mPreferencesHelper.writeXml(out, forBackup); - mListeners.writeXml(out, forBackup); - mAssistants.writeXml(out, forBackup); - mConditionProviders.writeXml(out, forBackup); - writeSecureNotificationsPolicy(out); + mZenModeHelper.writeXml(out, forBackup, null, userId); + mPreferencesHelper.writeXml(out, forBackup, userId); + mListeners.writeXml(out, forBackup, userId); + mAssistants.writeXml(out, forBackup, userId); + mConditionProviders.writeXml(out, forBackup, userId); + if (!forBackup || userId == UserHandle.USER_SYSTEM) { + writeSecureNotificationsPolicy(out); + } out.endTag(null, TAG_NOTIFICATION_POLICY); out.endDocument(); } @@ -1282,6 +1305,8 @@ public class NotificationManagerService extends SystemService { private final class SettingsObserver extends ContentObserver { private final Uri NOTIFICATION_BADGING_URI = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING); + private final Uri NOTIFICATION_BUBBLES_URI + = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BUBBLES); private final Uri NOTIFICATION_LIGHT_PULSE_URI = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); private final Uri NOTIFICATION_RATE_LIMIT_URI @@ -1299,6 +1324,8 @@ public class NotificationManagerService extends SystemService { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI, + false, this, UserHandle.USER_ALL); update(null); } @@ -1324,6 +1351,9 @@ public class NotificationManagerService extends SystemService { if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) { mPreferencesHelper.updateBadgingEnabled(); } + if (uri == null || NOTIFICATION_BUBBLES_URI.equals(uri)) { + mPreferencesHelper.updateBubblesEnabled(); + } } } @@ -3507,14 +3537,9 @@ public class NotificationManagerService extends SystemService { public byte[] getBackupPayload(int user) { checkCallerIsSystem(); if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); - //TODO: http://b/22388012 - if (user != USER_SYSTEM) { - Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); - return null; - } final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - writePolicyXml(baos, true /*forBackup*/); + writePolicyXml(baos, true /*forBackup*/, user); return baos.toByteArray(); } catch (IOException e) { Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); @@ -3531,14 +3556,9 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "applyRestore: no payload to restore for user " + user); return; } - //TODO: http://b/22388012 - if (user != USER_SYSTEM) { - Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); - return; - } final ByteArrayInputStream bais = new ByteArrayInputStream(payload); try { - readPolicyXml(bais, true /*forRestore*/); + readPolicyXml(bais, true /*forRestore*/, user); handleSavePolicyFile(); } catch (NumberFormatException | XmlPullParserException | IOException e) { Slog.w(TAG, "applyRestore: error reading payload", e); @@ -5849,6 +5869,7 @@ public class NotificationManagerService extends SystemService { ArrayList<String> orderBefore = new ArrayList<>(N); int[] visibilities = new int[N]; boolean[] showBadges = new boolean[N]; + boolean[] allowBubbles = new boolean[N]; ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N); ArrayList<String> groupKeyBefore = new ArrayList<>(N); ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N); @@ -5863,6 +5884,7 @@ public class NotificationManagerService extends SystemService { orderBefore.add(r.getKey()); visibilities[i] = r.getPackageVisibilityOverride(); showBadges[i] = r.canShowBadge(); + allowBubbles[i] = r.canBubble(); channelBefore.add(r.getChannel()); groupKeyBefore.add(r.getGroupKey()); overridePeopleBefore.add(r.getPeopleOverride()); @@ -5880,6 +5902,7 @@ public class NotificationManagerService extends SystemService { if (!orderBefore.get(i).equals(r.getKey()) || visibilities[i] != r.getPackageVisibilityOverride() || showBadges[i] != r.canShowBadge() + || allowBubbles[i] != r.canBubble() || !Objects.equals(channelBefore.get(i), r.getChannel()) || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey()) || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride()) @@ -6922,6 +6945,7 @@ public class NotificationManagerService extends SystemService { Bundle smartReplies = new Bundle(); Bundle lastAudiblyAlerted = new Bundle(); Bundle noisy = new Bundle(); + ArrayList<Boolean> canBubble = new ArrayList<>(N); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { @@ -6954,18 +6978,22 @@ public class NotificationManagerService extends SystemService { smartReplies.putCharSequenceArrayList(key, record.getSmartReplies()); lastAudiblyAlerted.putLong(key, record.getLastAudiblyAlertedMs()); noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null); + canBubble.add(record.canBubble()); } final int M = keys.size(); String[] keysAr = keys.toArray(new String[M]); String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); int[] importanceAr = new int[M]; + boolean[] canBubbleAr = new boolean[M]; for (int i = 0; i < M; i++) { importanceAr[i] = importance.get(i); + canBubbleAr[i] = canBubble.get(i); } return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden, - systemGeneratedSmartActions, smartReplies, lastAudiblyAlerted, noisy); + systemGeneratedSmartActions, smartReplies, lastAudiblyAlerted, noisy, + canBubbleAr); } boolean hasCompanionDevice(ManagedServiceInfo info) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index b3394b4c599f..48818f5649af 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -161,6 +161,7 @@ public final class NotificationRecord { private ArrayList<String> mPeopleOverride; private ArrayList<SnoozeCriterion> mSnoozeCriteria; private boolean mShowBadge; + private boolean mAllowBubble; private Light mLight; /** * This list contains system generated smart actions from NAS, app-generated smart actions are @@ -994,6 +995,14 @@ public final class NotificationRecord { mShowBadge = showBadge; } + public boolean canBubble() { + return mAllowBubble; + } + + public void setAllowBubble(boolean allow) { + mAllowBubble = allow; + } + public boolean canShowBadge() { return mShowBadge; } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 6ed4f5c03171..2c47ec03f9fa 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -127,6 +127,7 @@ public class PreferencesHelper implements RankingConfig { private final ZenModeHelper mZenModeHelper; private SparseBooleanArray mBadgingEnabled; + private SparseBooleanArray mBubblesEnabled; private boolean mAreChannelsBypassingDnd; private boolean mHideSilentStatusBarIcons; @@ -138,10 +139,11 @@ public class PreferencesHelper implements RankingConfig { mPm = pm; updateBadgingEnabled(); + updateBubblesEnabled(); syncChannelsBypassingDnd(mContext.getUserId()); } - public void readXml(XmlPullParser parser, boolean forRestore) + public void readXml(XmlPullParser parser, boolean forRestore, int userId) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return; @@ -158,6 +160,9 @@ public class PreferencesHelper implements RankingConfig { } if (type == XmlPullParser.START_TAG) { if (TAG_STATUS_ICONS.equals(tag)) { + if (forRestore && userId != UserHandle.USER_SYSTEM) { + continue; + } mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute( parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); } else if (TAG_PACKAGE.equals(tag)) { @@ -166,9 +171,7 @@ public class PreferencesHelper implements RankingConfig { if (!TextUtils.isEmpty(name)) { if (forRestore) { try { - //TODO: http://b/22388012 - uid = mPm.getPackageUidAsUser(name, - UserHandle.USER_SYSTEM); + uid = mPm.getPackageUidAsUser(name, userId); } catch (PackageManager.NameNotFoundException e) { // noop } @@ -379,10 +382,11 @@ public class PreferencesHelper implements RankingConfig { r.channels.put(channel.getId(), channel); } - public void writeXml(XmlSerializer out, boolean forBackup) throws IOException { + public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException { out.startTag(null, TAG_RANKING); out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION)); - if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS) { + if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS + && (!forBackup || userId == UserHandle.USER_SYSTEM)) { out.startTag(null, TAG_STATUS_ICONS); out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons)); out.endTag(null, TAG_STATUS_ICONS); @@ -392,8 +396,7 @@ public class PreferencesHelper implements RankingConfig { final int N = mPackagePreferences.size(); for (int i = 0; i < N; i++) { final PackagePreferences r = mPackagePreferences.valueAt(i); - //TODO: http://b/22388012 - if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) { + if (forBackup && UserHandle.getUserId(r.uid) != userId) { continue; } final boolean hasNonDefaultSettings = @@ -485,6 +488,7 @@ public class PreferencesHelper implements RankingConfig { * @param uid the uid to check if bubbles are allowed for. * @return whether bubbles are allowed. */ + @Override public boolean areBubblesAllowed(String pkg, int uid) { return getOrCreatePackagePreferences(pkg, uid).allowBubble; } @@ -1266,7 +1270,7 @@ public class PreferencesHelper implements RankingConfig { if (original.canShowBadge() != update.canShowBadge()) { update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); } - if (original.isBubbleAllowed() != update.isBubbleAllowed()) { + if (original.canBubble() != update.canBubble()) { update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE); } } @@ -1638,6 +1642,40 @@ public class PreferencesHelper implements RankingConfig { .setPackageName(pkg); } + public void updateBubblesEnabled() { + if (mBubblesEnabled == null) { + mBubblesEnabled = new SparseBooleanArray(); + } + boolean changed = false; + // update the cached values + for (int index = 0; index < mBubblesEnabled.size(); index++) { + int userId = mBubblesEnabled.keyAt(index); + final boolean oldValue = mBubblesEnabled.get(userId); + final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_BUBBLES, + DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0; + mBubblesEnabled.put(userId, newValue); + changed |= oldValue != newValue; + } + if (changed) { + updateConfig(); + } + } + + public boolean bubblesEnabled(UserHandle userHandle) { + int userId = userHandle.getIdentifier(); + if (userId == UserHandle.USER_ALL) { + return false; + } + if (mBubblesEnabled.indexOfKey(userId) < 0) { + mBubblesEnabled.put(userId, + Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_BUBBLES, + DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0); + } + return mBubblesEnabled.get(userId, DEFAULT_ALLOW_BUBBLE); + } + public void updateBadgingEnabled() { if (mBadgingEnabled == null) { diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 605348b4d52d..72502acd6560 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -29,6 +29,8 @@ public interface RankingConfig { void setShowBadge(String packageName, int uid, boolean showBadge); boolean canShowBadge(String packageName, int uid); boolean badgingEnabled(UserHandle userHandle); + boolean areBubblesAllowed(String packageName, int uid); + boolean bubblesEnabled(UserHandle userHandle); boolean isGroupBlocked(String packageName, int uid, String groupId); Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg, diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index afc0b7230e27..ea7bf2d23495 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -104,7 +104,7 @@ public class ZenModeHelper { protected final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate(); @VisibleForTesting protected final ZenModeConditions mConditions; - private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>(); + @VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>(); private final Metrics mMetrics = new Metrics(); private final ConditionProviders.Config mServiceConfig; @@ -662,17 +662,14 @@ public class ZenModeHelper { } } - public void readXml(XmlPullParser parser, boolean forRestore) + public void readXml(XmlPullParser parser, boolean forRestore, int userId) throws XmlPullParserException, IOException { ZenModeConfig config = ZenModeConfig.readXml(parser); String reason = "readXml"; if (config != null) { if (forRestore) { - //TODO: http://b/22388012 - if (config.user != UserHandle.USER_SYSTEM) { - return; - } + config.user = userId; config.manualRule = null; // don't restore the manual rule } @@ -707,13 +704,15 @@ public class ZenModeHelper { reason += ", reset to default rules"; } + // Resolve user id for settings. + userId = userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId; if (config.version < ZenModeConfig.XML_VERSION) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1, userId); } else { // devices not restoring/upgrading already have updated zen settings - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ZEN_SETTINGS_UPDATED, 1); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId); } if (DEBUG) Log.d(TAG, reason); synchronized (mConfig) { @@ -722,11 +721,11 @@ public class ZenModeHelper { } } - public void writeXml(XmlSerializer out, boolean forBackup, Integer version) throws IOException { + public void writeXml(XmlSerializer out, boolean forBackup, Integer version, int userId) + throws IOException { final int N = mConfigs.size(); for (int i = 0; i < N; i++) { - //TODO: http://b/22388012 - if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_SYSTEM) { + if (forBackup && mConfigs.keyAt(i) != userId) { continue; } mConfigs.valueAt(i).writeXml(out, version); diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index a7f114655dcb..e479a1539e25 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -23,6 +23,9 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.pm.PackageManager.SIGNATURE_MATCH; +import static android.os.Trace.TRACE_TAG_RRO; +import static android.os.Trace.traceBegin; +import static android.os.Trace.traceEnd; import android.annotation.NonNull; import android.annotation.Nullable; @@ -223,36 +226,41 @@ public final class OverlayManagerService extends SystemService { public OverlayManagerService(@NonNull final Context context, @NonNull final Installer installer) { super(context); - mSettingsFile = new AtomicFile( - new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays"); - mPackageManager = new PackageManagerHelper(); - mUserManager = UserManagerService.getInstance(); - IdmapManager im = new IdmapManager(installer); - mSettings = new OverlayManagerSettings(); - mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, - getDefaultOverlayPackages(), new OverlayChangeListener()); - - final IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(ACTION_PACKAGE_ADDED); - packageFilter.addAction(ACTION_PACKAGE_CHANGED); - packageFilter.addAction(ACTION_PACKAGE_REMOVED); - packageFilter.addDataScheme("package"); - getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, - packageFilter, null, null); - - final IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(ACTION_USER_ADDED); - userFilter.addAction(ACTION_USER_REMOVED); - getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL, - userFilter, null, null); - - restoreSettings(); - - initIfNeeded(); - onSwitchUser(UserHandle.USER_SYSTEM); - - publishBinderService(Context.OVERLAY_SERVICE, mService); - publishLocalService(OverlayManagerService.class, this); + try { + traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService"); + mSettingsFile = new AtomicFile( + new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays"); + mPackageManager = new PackageManagerHelper(); + mUserManager = UserManagerService.getInstance(); + IdmapManager im = new IdmapManager(installer); + mSettings = new OverlayManagerSettings(); + mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, + getDefaultOverlayPackages(), new OverlayChangeListener()); + + final IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(ACTION_PACKAGE_ADDED); + packageFilter.addAction(ACTION_PACKAGE_CHANGED); + packageFilter.addAction(ACTION_PACKAGE_REMOVED); + packageFilter.addDataScheme("package"); + getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, + packageFilter, null, null); + + final IntentFilter userFilter = new IntentFilter(); + userFilter.addAction(ACTION_USER_ADDED); + userFilter.addAction(ACTION_USER_REMOVED); + getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL, + userFilter, null, null); + + restoreSettings(); + + initIfNeeded(); + onSwitchUser(UserHandle.USER_SYSTEM); + + publishBinderService(Context.OVERLAY_SERVICE, mService); + publishLocalService(OverlayManagerService.class, this); + } finally { + traceEnd(TRACE_TAG_RRO); + } } @Override @@ -280,13 +288,18 @@ public final class OverlayManagerService extends SystemService { @Override public void onSwitchUser(final int newUserId) { - // ensure overlays in the settings are up-to-date, and propagate - // any asset changes to the rest of the system - synchronized (mLock) { - final List<String> targets = mImpl.updateOverlaysForUser(newUserId); - updateAssets(newUserId, targets); + try { + traceBegin(TRACE_TAG_RRO, "OMS#onSwitchUser " + newUserId); + // ensure overlays in the settings are up-to-date, and propagate + // any asset changes to the rest of the system + synchronized (mLock) { + final List<String> targets = mImpl.updateOverlaysForUser(newUserId); + updateAssets(newUserId, targets); + } + schedulePersistSettings(); + } finally { + traceEnd(TRACE_TAG_RRO); } - schedulePersistSettings(); } private static String[] getDefaultOverlayPackages() { @@ -350,85 +363,110 @@ public final class OverlayManagerService extends SystemService { private void onPackageAdded(@NonNull final String packageName, @NonNull final int[] userIds) { - for (final int userId : userIds) { - synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageAdded(packageName, userId); - } else { - mImpl.onTargetPackageAdded(packageName, userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName); + for (final int userId : userIds) { + synchronized (mLock) { + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, + false); + if (pi != null) { + mPackageManager.cachePackageInfo(packageName, userId, pi); + if (pi.isOverlayPackage()) { + mImpl.onOverlayPackageAdded(packageName, userId); + } else { + mImpl.onTargetPackageAdded(packageName, userId); + } } } } + } finally { + traceEnd(TRACE_TAG_RRO); } } private void onPackageChanged(@NonNull final String packageName, @NonNull final int[] userIds) { - for (int userId : userIds) { - synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageChanged(packageName, userId); - } else { - mImpl.onTargetPackageChanged(packageName, userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, + false); + if (pi != null) { + mPackageManager.cachePackageInfo(packageName, userId, pi); + if (pi.isOverlayPackage()) { + mImpl.onOverlayPackageChanged(packageName, userId); + } else { + mImpl.onTargetPackageChanged(packageName, userId); + } } } } + } finally { + traceEnd(TRACE_TAG_RRO); } } private void onPackageUpgrading(@NonNull final String packageName, @NonNull final int[] userIds) { - for (int userId : userIds) { - synchronized (mLock) { - mPackageManager.forgetPackageInfo(packageName, userId); - final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi != null) { - mImpl.onOverlayPackageUpgrading(packageName, userId); - } else { - mImpl.onTargetPackageUpgrading(packageName, userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageUpgrading " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + mPackageManager.forgetPackageInfo(packageName, userId); + final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); + if (oi != null) { + mImpl.onOverlayPackageUpgrading(packageName, userId); + } else { + mImpl.onTargetPackageUpgrading(packageName, userId); + } } } + } finally { + traceEnd(TRACE_TAG_RRO); } } private void onPackageUpgraded(@NonNull final String packageName, @NonNull final int[] userIds) { - for (int userId : userIds) { - synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageUpgraded(packageName, userId); - } else { - mImpl.onTargetPackageUpgraded(packageName, userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageUpgraded " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, + false); + if (pi != null) { + mPackageManager.cachePackageInfo(packageName, userId, pi); + if (pi.isOverlayPackage()) { + mImpl.onOverlayPackageUpgraded(packageName, userId); + } else { + mImpl.onTargetPackageUpgraded(packageName, userId); + } } } } + } finally { + traceEnd(TRACE_TAG_RRO); } } private void onPackageRemoved(@NonNull final String packageName, @NonNull final int[] userIds) { - for (int userId : userIds) { - synchronized (mLock) { - mPackageManager.forgetPackageInfo(packageName, userId); - final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi != null) { - mImpl.onOverlayPackageRemoved(packageName, userId); - } else { - mImpl.onTargetPackageRemoved(packageName, userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + mPackageManager.forgetPackageInfo(packageName, userId); + final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); + if (oi != null) { + mImpl.onOverlayPackageRemoved(packageName, userId); + } else { + mImpl.onTargetPackageRemoved(packageName, userId); + } } } + } finally { + traceEnd(TRACE_TAG_RRO); } } } @@ -440,19 +478,29 @@ public final class OverlayManagerService extends SystemService { switch (intent.getAction()) { case ACTION_USER_ADDED: if (userId != UserHandle.USER_NULL) { - final ArrayList<String> targets; - synchronized (mLock) { - targets = mImpl.updateOverlaysForUser(userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED"); + final ArrayList<String> targets; + synchronized (mLock) { + targets = mImpl.updateOverlaysForUser(userId); + } + updateOverlayPaths(userId, targets); + } finally { + traceEnd(TRACE_TAG_RRO); } - updateOverlayPaths(userId, targets); } break; case ACTION_USER_REMOVED: if (userId != UserHandle.USER_NULL) { - synchronized (mLock) { - mImpl.onUserRemoved(userId); - mPackageManager.forgetAllPackageInfos(userId); + try { + traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_REMOVED"); + synchronized (mLock) { + mImpl.onUserRemoved(userId); + mPackageManager.forgetAllPackageInfos(userId); + } + } finally { + traceEnd(TRACE_TAG_RRO); } } break; @@ -466,152 +514,198 @@ public final class OverlayManagerService extends SystemService { private final IBinder mService = new IOverlayManager.Stub() { @Override public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException { - userId = handleIncomingUser(userId, "getAllOverlays"); + try { + traceBegin(TRACE_TAG_RRO, "OMS#getAllOverlays " + userId); + userId = handleIncomingUser(userId, "getAllOverlays"); - synchronized (mLock) { - return mImpl.getOverlaysForUser(userId); + synchronized (mLock) { + return mImpl.getOverlaysForUser(userId); + } + } finally { + traceEnd(TRACE_TAG_RRO); } } @Override public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName, int userId) throws RemoteException { - userId = handleIncomingUser(userId, "getOverlayInfosForTarget"); - if (targetPackageName == null) { - return Collections.emptyList(); - } + try { + traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfosForTarget " + targetPackageName); + userId = handleIncomingUser(userId, "getOverlayInfosForTarget"); + if (targetPackageName == null) { + return Collections.emptyList(); + } - synchronized (mLock) { - return mImpl.getOverlayInfosForTarget(targetPackageName, userId); + synchronized (mLock) { + return mImpl.getOverlayInfosForTarget(targetPackageName, userId); + } + } finally { + traceEnd(TRACE_TAG_RRO); } } @Override public OverlayInfo getOverlayInfo(@Nullable final String packageName, int userId) throws RemoteException { - userId = handleIncomingUser(userId, "getOverlayInfo"); - if (packageName == null) { - return null; - } + try { + traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName); + userId = handleIncomingUser(userId, "getOverlayInfo"); + if (packageName == null) { + return null; + } - synchronized (mLock) { - return mImpl.getOverlayInfo(packageName, userId); + synchronized (mLock) { + return mImpl.getOverlayInfo(packageName, userId); + } + } finally { + traceEnd(TRACE_TAG_RRO); } } @Override public boolean setEnabled(@Nullable final String packageName, final boolean enable, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setEnabled"); - userId = handleIncomingUser(userId, "setEnabled"); - if (packageName == null) { - return false; - } - - final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - return mImpl.setEnabled(packageName, enable, userId); + traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable); + enforceChangeOverlayPackagesPermission("setEnabled"); + userId = handleIncomingUser(userId, "setEnabled"); + if (packageName == null) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setEnabled(packageName, enable, userId); + } + } finally { + Binder.restoreCallingIdentity(ident); } } finally { - Binder.restoreCallingIdentity(ident); + traceEnd(TRACE_TAG_RRO); } } @Override public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setEnabledExclusive"); - userId = handleIncomingUser(userId, "setEnabledExclusive"); - if (packageName == null || !enable) { - return false; - } - - final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - return mImpl.setEnabledExclusive(packageName, false /* withinCategory */, - userId); + traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable); + enforceChangeOverlayPackagesPermission("setEnabledExclusive"); + userId = handleIncomingUser(userId, "setEnabledExclusive"); + if (packageName == null || !enable) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setEnabledExclusive(packageName, false /* withinCategory */, + userId); + } + } finally { + Binder.restoreCallingIdentity(ident); } } finally { - Binder.restoreCallingIdentity(ident); + traceEnd(TRACE_TAG_RRO); } } @Override public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory"); - userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory"); - if (packageName == null) { - return false; - } - - final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - return mImpl.setEnabledExclusive(packageName, true /* withinCategory */, - userId); + traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName); + enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory"); + userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory"); + if (packageName == null) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setEnabledExclusive(packageName, true /* withinCategory */, + userId); + } + } finally { + Binder.restoreCallingIdentity(ident); } } finally { - Binder.restoreCallingIdentity(ident); + traceEnd(TRACE_TAG_RRO); } } @Override public boolean setPriority(@Nullable final String packageName, @Nullable final String parentPackageName, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setPriority"); - userId = handleIncomingUser(userId, "setPriority"); - if (packageName == null || parentPackageName == null) { - return false; - } - - final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - return mImpl.setPriority(packageName, parentPackageName, userId); + traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " " + + parentPackageName); + enforceChangeOverlayPackagesPermission("setPriority"); + userId = handleIncomingUser(userId, "setPriority"); + if (packageName == null || parentPackageName == null) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setPriority(packageName, parentPackageName, userId); + } + } finally { + Binder.restoreCallingIdentity(ident); } } finally { - Binder.restoreCallingIdentity(ident); + traceEnd(TRACE_TAG_RRO); } } @Override public boolean setHighestPriority(@Nullable final String packageName, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setHighestPriority"); - userId = handleIncomingUser(userId, "setHighestPriority"); - if (packageName == null) { - return false; - } - - final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - return mImpl.setHighestPriority(packageName, userId); + traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName); + enforceChangeOverlayPackagesPermission("setHighestPriority"); + userId = handleIncomingUser(userId, "setHighestPriority"); + if (packageName == null) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setHighestPriority(packageName, userId); + } + } finally { + Binder.restoreCallingIdentity(ident); } } finally { - Binder.restoreCallingIdentity(ident); + traceEnd(TRACE_TAG_RRO); } } @Override public boolean setLowestPriority(@Nullable final String packageName, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setLowestPriority"); - userId = handleIncomingUser(userId, "setLowestPriority"); - if (packageName == null) { - return false; - } - - final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - return mImpl.setLowestPriority(packageName, userId); + traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName); + enforceChangeOverlayPackagesPermission("setLowestPriority"); + userId = handleIncomingUser(userId, "setLowestPriority"); + if (packageName == null) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setLowestPriority(packageName, userId); + } + } finally { + Binder.restoreCallingIdentity(ident); } } finally { - Binder.restoreCallingIdentity(ident); + traceEnd(TRACE_TAG_RRO); } } @@ -705,45 +799,52 @@ public final class OverlayManagerService extends SystemService { * Updates the target packages' set of enabled overlays in PackageManager. */ private void updateOverlayPaths(int userId, List<String> targetPackageNames) { - if (DEBUG) { - Slog.d(TAG, "Updating overlay assets"); - } - final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); - final boolean updateFrameworkRes = targetPackageNames.contains("android"); - if (updateFrameworkRes) { - targetPackageNames = pm.getTargetPackageNames(userId); - } + try { + traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames); + if (DEBUG) { + Slog.d(TAG, "Updating overlay assets"); + } + final PackageManagerInternal pm = + LocalServices.getService(PackageManagerInternal.class); + final boolean updateFrameworkRes = targetPackageNames.contains("android"); + if (updateFrameworkRes) { + targetPackageNames = pm.getTargetPackageNames(userId); + } + + final Map<String, List<String>> pendingChanges = + new ArrayMap<>(targetPackageNames.size()); + synchronized (mLock) { + final List<String> frameworkOverlays = + mImpl.getEnabledOverlayPackageNames("android", userId); + final int n = targetPackageNames.size(); + for (int i = 0; i < n; i++) { + final String targetPackageName = targetPackageNames.get(i); + List<String> list = new ArrayList<>(); + if (!"android".equals(targetPackageName)) { + list.addAll(frameworkOverlays); + } + list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); + pendingChanges.put(targetPackageName, list); + } + } - final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size()); - synchronized (mLock) { - final List<String> frameworkOverlays = - mImpl.getEnabledOverlayPackageNames("android", userId); final int n = targetPackageNames.size(); for (int i = 0; i < n; i++) { final String targetPackageName = targetPackageNames.get(i); - List<String> list = new ArrayList<>(); - if (!"android".equals(targetPackageName)) { - list.addAll(frameworkOverlays); + if (DEBUG) { + Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" + + TextUtils.join(",", pendingChanges.get(targetPackageName)) + + "] userId=" + userId); } - list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); - pendingChanges.put(targetPackageName, list); - } - } - - final int n = targetPackageNames.size(); - for (int i = 0; i < n; i++) { - final String targetPackageName = targetPackageNames.get(i); - if (DEBUG) { - Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" - + TextUtils.join(",", pendingChanges.get(targetPackageName)) - + "] userId=" + userId); - } - if (!pm.setEnabledOverlayPackages( - userId, targetPackageName, pendingChanges.get(targetPackageName))) { - Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", - targetPackageName, userId)); + if (!pm.setEnabledOverlayPackages( + userId, targetPackageName, pendingChanges.get(targetPackageName))) { + Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", + targetPackageName, userId)); + } } + } finally { + traceEnd(TRACE_TAG_RRO); } } @@ -785,32 +886,37 @@ public final class OverlayManagerService extends SystemService { } private void restoreSettings() { - synchronized (mLock) { - if (!mSettingsFile.getBaseFile().exists()) { - return; - } - try (FileInputStream stream = mSettingsFile.openRead()) { - mSettings.restore(stream); + try { + traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings"); + synchronized (mLock) { + if (!mSettingsFile.getBaseFile().exists()) { + return; + } + try (FileInputStream stream = mSettingsFile.openRead()) { + mSettings.restore(stream); - // We might have data for dying users if the device was - // restarted before we received USER_REMOVED. Remove data for - // users that will not exist after the system is ready. + // We might have data for dying users if the device was + // restarted before we received USER_REMOVED. Remove data for + // users that will not exist after the system is ready. - final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/); - final int[] liveUserIds = new int[liveUsers.size()]; - for (int i = 0; i < liveUsers.size(); i++) { - liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier(); - } - Arrays.sort(liveUserIds); + final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/); + final int[] liveUserIds = new int[liveUsers.size()]; + for (int i = 0; i < liveUsers.size(); i++) { + liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier(); + } + Arrays.sort(liveUserIds); - for (int userId : mSettings.getUsers()) { - if (Arrays.binarySearch(liveUserIds, userId) < 0) { - mSettings.removeUser(userId); + for (int userId : mSettings.getUsers()) { + if (Arrays.binarySearch(liveUserIds, userId) < 0) { + mSettings.removeUser(userId); + } } + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "failed to restore overlay state", e); } - } catch (IOException | XmlPullParserException e) { - Slog.e(TAG, "failed to restore overlay state", e); } + } finally { + traceEnd(TRACE_TAG_RRO); } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index efafdfaf2b54..c2a75aba28b9 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -611,18 +611,43 @@ public class Installer extends SystemService { } } - public boolean snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags) + /** + * Snapshots user data of the given package. + * + * @param pkg name of the package to snapshot user data for. + * @param userId id of the user whose data to snapshot. + * @param storageFlags flags controlling which data (CE or DE) to snapshot. + * + * @return inode of the snapshot of users CE package data, or {@code 0} if a remote calls + * shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to snapshot user data. + */ + public long snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags) throws InstallerException { - if (!checkBeforeRemote()) return false; + if (!checkBeforeRemote()) return 0; try { - mInstalld.snapshotAppData(null, pkg, userId, storageFlags); - return true; + return mInstalld.snapshotAppData(null, pkg, userId, storageFlags); } catch (Exception e) { throw InstallerException.from(e); } } + /** + * Restores user data snapshot of the given package. + * + * @param pkg name of the package to restore user data for. + * @param appId id of the package to restore user data for. + * @param ceDataInode inode of CE user data folder of this app. + * @param userId id of the user whose data to restore. + * @param storageFlags flags controlling which data (CE or DE) to restore. + * + * @return {@code true} if user data restore was successful, or {@code false} if a remote call + * shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to restore user data. + */ public boolean restoreAppDataSnapshot(String pkg, @AppIdInt int appId, long ceDataInode, String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException { if (!checkBeforeRemote()) return false; @@ -636,6 +661,31 @@ public class Installer extends SystemService { } } + /** + * Deletes user data snapshot of the given package. + * + * @param pkg name of the package to delete user data snapshot for. + * @param userId id of the user whose user data snapshot to delete. + * @param ceSnapshotInode inode of CE user data snapshot. + * @param storageFlags flags controlling which user data snapshot (CE or DE) to delete. + * + * @return {@code true} if user data snapshot was successfully deleted, or {@code false} if a + * remote call shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to delete user data snapshot. + */ + public boolean destroyAppDataSnapshot(String pkg, @UserIdInt int userId, long ceSnapshotInode, + int storageFlags) throws InstallerException { + if (!checkBeforeRemote()) return false; + + try { + mInstalld.destroyAppDataSnapshot(null, pkg, userId, ceSnapshotInode, storageFlags); + return true; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + private static void assertValidInstructionSet(String instructionSet) throws InstallerException { for (String abi : Build.SUPPORTED_ABIS) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d06fc513c0e8..1b719048bc89 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -44,7 +44,6 @@ import static com.android.server.pm.PackageInstallerService.prepareStageDir; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.apex.IApexService; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; import android.content.IIntentReceiver; @@ -80,7 +79,6 @@ import android.os.ParcelableException; import android.os.Process; import android.os.RemoteException; import android.os.RevocableFileDescriptor; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; @@ -1084,6 +1082,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); return; } + if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_INTERNAL_ERROR, + "APEX packages can only be installed using staged sessions."); + } final PackageManagerService.ActiveInstallSession committingSession = makeSessionActiveLocked(); if (committingSession == null) { @@ -1101,12 +1104,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final PackageManagerService.ActiveInstallSession activeSession = session.makeSessionActiveLocked(); if (activeSession != null) { - if ((activeSession.getSessionParams().installFlags - & PackageManager.INSTALL_APEX) != 0) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_INTERNAL_ERROR, - "Atomic install is not supported for APEX packages."); - } childSessions.add(activeSession); } } catch (PackageManagerException e) { @@ -1124,27 +1121,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } mPm.installStage(childSessions); } else { - if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { - commitApexLocked(); - } else { - mPm.installStage(committingSession); - } - } - } - - @GuardedBy("mLock") - private void commitApexLocked() throws PackageManagerException { - try { - IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - apex.stagePackage(mResolvedBaseFile.toString()); - } catch (Throwable e) { - // Convert all exceptions into package manager exceptions as only those are handled - // in the code above - throw new PackageManagerException(e); - } finally { - destroyInternal(); - dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null); + mPm.installStage(committingSession); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e18da7f7b319..874d1a719ee6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -195,7 +195,6 @@ import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.UsesPermissionInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.content.pm.VersionedPackage; @@ -11313,26 +11312,6 @@ public class PackageManagerService extends IPackageManager.Stub } } } - - // Check permission usage info requirements. - if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) { - for (UsesPermissionInfo upi : pkg.usesPermissionInfos) { - if (!mPermissionManager.isPermissionUsageInfoRequired(upi.getPermission())) { - continue; - } - if (upi.getDataSentOffDevice() == UsesPermissionInfo.USAGE_UNDEFINED - || upi.getDataSharedWithThirdParty() - == UsesPermissionInfo.USAGE_UNDEFINED - || upi.getDataUsedForMonetization() - == UsesPermissionInfo.USAGE_UNDEFINED - || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) { - // STOPSHIP: Make this throw - Slog.e(TAG, "Package " + pkg.packageName + " does not provide usage " - + "information for permission " + upi.getPermission() - + ". This will be a fatal error in Q."); - } - } - } } } @@ -14613,6 +14592,13 @@ public class PackageManagerService extends IPackageManager.Stub PACKAGE_MIME_TYPE); enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + // Allow the broadcast to be sent before boot complete. + // This is needed when committing the apk part of a staged + // session in early boot. The rollback manager registers + // its receiver early enough during the boot process that + // it will not miss the broadcast. + enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, getUser(), android.Manifest.permission.PACKAGE_ROLLBACK_AGENT, new BroadcastReceiver() { @@ -16335,7 +16321,6 @@ public class PackageManagerService extends IPackageManager.Stub final boolean onExternal = args.volumeUuid != null; final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0); - final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0); final boolean virtualPreload = ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; @@ -16367,8 +16352,7 @@ public class PackageManagerService extends IPackageManager.Stub // Retrieve PackageSettings and parse package @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | PackageParser.PARSE_ENFORCE_CODE - | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0) - | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0); + | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); @@ -16789,19 +16773,6 @@ public class PackageManagerService extends IPackageManager.Stub "replacePackageLI: new=" + pkg + ", old=" + oldPackage); } - // don't allow upgrade to target a release SDK from a pre-release SDK - final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion - == Build.VERSION_CODES.CUR_DEVELOPMENT; - final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion - == Build.VERSION_CODES.CUR_DEVELOPMENT; - if (oldTargetsPreRelease - && !newTargetsPreRelease - && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) { - Slog.w(TAG, "Can't install package targeting released sdk"); - throw new PrepareFailure( - PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); - } - ps = mSettings.mPackages.get(pkgName11); disabledPs = mSettings.getDisabledSystemPkgLPr(ps); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index dc18dfcf8613..2eb762b59be4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2383,8 +2383,7 @@ class PackageManagerShellCommand extends ShellCommand { sessionParams.volumeUuid = null; } break; - case "--force-sdk": - sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK; + case "--force-sdk": // ignore break; case "--apex": sessionParams.setInstallAsApex(); @@ -2961,8 +2960,6 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" 0=unknown, 1=admin policy, 2=device restore,"); pw.println(" 3=device setup, 4=user request"); pw.println(" --force-uuid: force install on to disk volume with given UUID"); - pw.println(" --force-sdk: allow install even when existing app targets platform"); - pw.println(" codename but new one targets a final API level"); pw.println(" --apex: install an .apex file, not an .apk"); pw.println(""); pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]"); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 55eb7ea7da4d..fa8360b1e5ba 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -220,6 +220,7 @@ public class StagingManager { session.setStagedSessionFailed( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more details."); + return; } if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) { @@ -380,6 +381,19 @@ public class StagingManager { private boolean commitApkSession(@NonNull PackageInstallerSession apkSession, int originalSessionId) { + + if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { + // If rollback is available for this session, notify the rollback + // manager of the apk session so it can properly enable rollback. + final IRollbackManager rm = IRollbackManager.Stub.asInterface( + ServiceManager.getService(Context.ROLLBACK_SERVICE)); + try { + rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId); + } catch (RemoteException re) { + // Cannot happen, the rollback manager is in the same process. + } + } + final LocalIntentReceiver receiver = new LocalIntentReceiver(); apkSession.commit(receiver.getIntentSender(), false); final Intent result = receiver.getResult(); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 8d64b810b407..18e292feba30 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -743,6 +743,9 @@ public class UserRestrictionsUtils { case android.provider.Settings.Global.PRIVATE_DNS_MODE: case android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER: + if (callingUid == Process.SYSTEM_UID) { + return false; + } restriction = UserManager.DISALLOW_CONFIG_PRIVATE_DNS; break; default: diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 23705db884f5..9948a3ad47da 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -480,10 +480,13 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { final String apkPath = pkg.baseCodePath; final ApplicationInfo appInfo = pkg.applicationInfo; final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex"; - if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed()) { + if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed() + || appInfo.isDefaultToDeviceProtectedStorage()) { // Privileged apps prefer to load trusted code so they don't use compiled views. // If the app is not privileged but prefers code integrity, also avoid compiling // views. + // Also disable the view compiler for protected storage apps since there are + // selinux permissions required for writing to user_de. return false; } Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath + diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 2213901b2049..ee6995b11430 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -228,8 +228,11 @@ public class DexManager { continue; } - mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName, - loadingAppInfo.packageName); + if (!primaryOrSplit) { + // Record loading of a DEX file from an app data directory. + mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName, + loadingAppInfo.packageName); + } if (classLoaderContexts != null) { diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 173d9a0551df..1957eb89fd0c 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -105,8 +105,6 @@ public final class BasePermission { */ private boolean perUser; - boolean usageInfoRequired; - public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) { name = _name; sourcePackageName = _sourcePackageName; @@ -375,7 +373,6 @@ public final class BasePermission { } if (bp.perm == p) { bp.protectionLevel = p.info.protectionLevel; - bp.usageInfoRequired = p.info.usageInfoRequired; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { Log.d(TAG, " Permissions: " + r); @@ -455,7 +452,6 @@ public final class BasePermission { permissionInfo.packageName = sourcePackageName; permissionInfo.nonLocalizedLabel = name; permissionInfo.protectionLevel = protectionLevel; - permissionInfo.usageInfoRequired = usageInfoRequired; return permissionInfo; } @@ -484,7 +480,6 @@ public final class BasePermission { bp.protectionLevel = readInt(parser, null, "protection", PermissionInfo.PROTECTION_NORMAL); bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel); - bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0; if (dynamic) { final PermissionInfo pi = new PermissionInfo(); pi.packageName = sourcePackage.intern(); @@ -492,7 +487,6 @@ public final class BasePermission { pi.icon = readInt(parser, null, "icon", 0); pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); pi.protectionLevel = bp.protectionLevel; - pi.usageInfoRequired = bp.usageInfoRequired; bp.pendingPermissionInfo = pi; } out.put(bp.name, bp); @@ -525,7 +519,6 @@ public final class BasePermission { if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) { serializer.attribute(null, "protection", Integer.toString(protectionLevel)); } - serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0"); if (type == BasePermission.TYPE_DYNAMIC) { final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo; if (pi != null) { @@ -562,7 +555,6 @@ public final class BasePermission { if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; // We'll take care of setting this one. if (!compareStrings(pi1.packageName, pi2.packageName)) return false; - if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false; // These are not currently stored in settings. //if (!compareStrings(pi1.group, pi2.group)) return false; //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; @@ -610,8 +602,6 @@ public final class BasePermission { pw.print(" enforced="); pw.println(readEnforced); } - pw.print(" usageInfoRequired="); - pw.println(usageInfoRequired); return true; } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java index 189d0f476a8c..f4979746bae3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java @@ -181,9 +181,4 @@ public abstract class PermissionManagerInternal { /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); - - /** - * Returns {@code true} if {@code permName} has {@code usageInfoRequired} set. - */ - public abstract boolean isPermissionUsageInfoRequired(@NonNull String permName); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a4413f963642..38940d6241a6 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2737,12 +2737,5 @@ public class PermissionManagerService { return mSettings.getPermissionLocked(permName); } } - @Override - public boolean isPermissionUsageInfoRequired(String permName) { - synchronized (PermissionManagerService.this.mLock) { - BasePermission bp = mSettings.getPermissionLocked(permName); - return bp != null && bp.usageInfoRequired; - } - } } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0796a9c896a3..b00193f5453f 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -148,6 +148,7 @@ import android.os.IBinder; import android.os.IDeviceIdleController; import android.os.Message; import android.os.PowerManager; +import android.os.PowerManager.WakeReason; import android.os.PowerManagerInternal; import android.os.Process; import android.os.RemoteException; @@ -189,6 +190,7 @@ import android.view.ViewConfiguration; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; +import android.view.WindowManagerPolicyConstants; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; @@ -809,7 +811,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false, "Wake Up"); wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, - "android.policy:GESTURE"); + PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE"); } } } @@ -3527,7 +3529,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (lidOpen) { wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch, - "android.policy:LID"); + PowerManager.WAKE_REASON_LID, "android.policy:LID"); } else if (!mLidControlsSleep) { mPowerManager.userActivity(SystemClock.uptimeMillis(), false); } @@ -3550,7 +3552,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); } wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens, - "android.policy:CAMERA_COVER"); + PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER"); startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); } mCameraLensCoverState = lensCoverState; @@ -3679,7 +3681,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isValidGlobalKey(keyCode) && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) { if (isWakeKey) { - wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY"); + wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, + PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY"); } return result; } @@ -4025,7 +4028,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (isWakeKey) { - wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY"); + wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, + PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY"); } return result; @@ -4125,7 +4129,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { if ((policyFlags & FLAG_WAKE) != 0) { if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion, - "android.policy:MOTION")) { + PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) { return 0; } } @@ -4139,7 +4143,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // wake up in this case. if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) { wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming, - "android.policy:MOTION"); + PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION"); } return 0; @@ -4371,7 +4375,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the PowerManager's Notifier thread. @Override public void startedGoingToSleep(int why) { - if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")"); + if (DEBUG_WAKEUP) { + Slog.i(TAG, "Started going to sleep... (why=" + + WindowManagerPolicyConstants.offReasonToString(why) + ")"); + } mGoingToSleep = true; mRequestedOrGoingToSleep = true; @@ -4385,7 +4392,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void finishedGoingToSleep(int why) { EventLog.writeEvent(70000, 0); - if (DEBUG_WAKEUP) Slog.i(TAG, "Finished going to sleep... (why=" + why + ")"); + if (DEBUG_WAKEUP) { + Slog.i(TAG, "Finished going to sleep... (why=" + + WindowManagerPolicyConstants.offReasonToString(why) + ")"); + } MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000); mGoingToSleep = false; @@ -4409,9 +4419,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the PowerManager's Notifier thread. @Override - public void startedWakingUp() { + public void startedWakingUp(@OnReason int why) { EventLog.writeEvent(70000, 1); - if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up..."); + if (DEBUG_WAKEUP) { + Slog.i(TAG, "Started waking up... (why=" + + WindowManagerPolicyConstants.onReasonToString(why) + ")"); + } mDefaultDisplayPolicy.setAwake(true); @@ -4432,8 +4445,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the PowerManager's Notifier thread. @Override - public void finishedWakingUp() { - if (DEBUG_WAKEUP) Slog.i(TAG, "Finished waking up..."); + public void finishedWakingUp(@OnReason int why) { + if (DEBUG_WAKEUP) { + Slog.i(TAG, "Finished waking up... (why=" + + WindowManagerPolicyConstants.onReasonToString(why) + ")"); + } if (mKeyguardDelegate != null) { mKeyguardDelegate.onFinishedWakingUp(); @@ -4441,10 +4457,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void wakeUpFromPowerKey(long eventTime) { - wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER"); + wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, + PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER"); } - private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) { + private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, + String details) { final boolean theaterModeEnabled = isTheaterModeEnabled(); if (!wakeInTheaterMode && theaterModeEnabled) { return false; @@ -4455,7 +4473,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.THEATER_MODE_ON, 0); } - mPowerManager.wakeUp(wakeTime, reason); + mPowerManager.wakeUp(wakeTime, reason, details); return true; } @@ -4786,7 +4804,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate.onBootCompleted(); } } - startedWakingUp(); + startedWakingUp(ON_BECAUSE_OF_UNKNOWN); screenTurningOn(null); screenTurnedOn(); } diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index e18cd179b113..d1bd102f11dc 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1070,12 +1070,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Called when the device has started waking up. */ - public void startedWakingUp(); + void startedWakingUp(@OnReason int reason); /** * Called when the device has finished waking up. */ - public void finishedWakingUp(); + void finishedWakingUp(@OnReason int reason); /** * Called when the device has started going to sleep. diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java index e7de8ddf87d1..3534cf30e2bf 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -67,8 +67,8 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder mContext.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION, userId); - // TODO: STOPSHIP: Remove the following code once we remove default_sms_application - // and use the new config_defaultRoleHolders. + // TODO: STOPSHIP: Remove the following code once we read the value of + // config_defaultSms in RoleControllerService. if (result == null) { Collection<SmsApplication.SmsApplicationData> applications = SmsApplication.getApplicationCollectionAsUser(mContext, userId); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index c3f20aab568e..1a82858147f4 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PowerManager; +import android.os.PowerManager.WakeReason; import android.os.PowerManagerInternal; import android.os.Process; import android.os.RemoteException; @@ -48,6 +49,7 @@ import android.provider.Settings; import android.util.EventLog; import android.util.Slog; import android.util.StatsLog; +import android.view.WindowManagerPolicyConstants.OnReason; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; @@ -136,6 +138,7 @@ public class Notifier { // broadcasted state over the course of reporting the transition asynchronously. private boolean mInteractive = true; private int mInteractiveChangeReason; + private long mInteractiveChangeStartTime; // In SystemClock.uptimeMillis() private boolean mInteractiveChanging; // The pending interactive state that we will eventually want to broadcast. @@ -371,7 +374,7 @@ public class Notifier { * which case it will assume that the state did not fully converge before the * next transition began and will recover accordingly. */ - public void onWakefulnessChangeStarted(final int wakefulness, int reason) { + public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) { final boolean interactive = PowerManagerInternal.isInteractive(wakefulness); if (DEBUG) { Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness @@ -410,6 +413,7 @@ public class Notifier { // Handle early behaviors. mInteractive = interactive; mInteractiveChangeReason = reason; + mInteractiveChangeStartTime = eventTime; mInteractiveChanging = true; handleEarlyInteractiveChange(); } @@ -440,8 +444,8 @@ public class Notifier { mHandler.post(new Runnable() { @Override public void run() { - // Note a SCREEN tron event is logged in PowerManagerService. - mPolicy.startedWakingUp(); + final int why = translateOnReason(mInteractiveChangeReason); + mPolicy.startedWakingUp(why); } }); @@ -470,12 +474,21 @@ public class Notifier { */ private void handleLateInteractiveChange() { synchronized (mLock) { + final int interactiveChangeLatency = + (int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime); if (mInteractive) { // Finished waking up... + final int why = translateOnReason(mInteractiveChangeReason); mHandler.post(new Runnable() { @Override public void run() { - mPolicy.finishedWakingUp(); + LogMaker log = new LogMaker(MetricsEvent.SCREEN); + log.setType(MetricsEvent.TYPE_OPEN); + log.setSubtype(why); + log.setLatency(interactiveChangeLatency); + MetricsLogger.action(log); + EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency); + mPolicy.finishedWakingUp(why); } }); } else { @@ -499,8 +512,9 @@ public class Notifier { LogMaker log = new LogMaker(MetricsEvent.SCREEN); log.setType(MetricsEvent.TYPE_CLOSE); log.setSubtype(why); + log.setLatency(interactiveChangeLatency); MetricsLogger.action(log); - EventLogTags.writePowerScreenState(0, why, 0, 0, 0); + EventLogTags.writePowerScreenState(0, why, 0, 0, interactiveChangeLatency); mPolicy.finishedGoingToSleep(why); } }); @@ -524,6 +538,23 @@ public class Notifier { } } + private static @OnReason int translateOnReason(@WakeReason int reason) { + switch (reason) { + case PowerManager.WAKE_REASON_POWER_BUTTON: + case PowerManager.WAKE_REASON_PLUGGED_IN: + case PowerManager.WAKE_REASON_GESTURE: + case PowerManager.WAKE_REASON_CAMERA_LAUNCH: + case PowerManager.WAKE_REASON_WAKE_KEY: + case PowerManager.WAKE_REASON_WAKE_MOTION: + case PowerManager.WAKE_REASON_LID: + return WindowManagerPolicy.ON_BECAUSE_OF_USER; + case PowerManager.WAKE_REASON_APPLICATION: + return WindowManagerPolicy.ON_BECAUSE_OF_APPLICATION; + default: + return WindowManagerPolicy.ON_BECAUSE_OF_UNKNOWN; + } + } + /** * Called when screen brightness boost begins or ends. */ @@ -565,14 +596,16 @@ public class Notifier { /** * Called when the screen has turned on. */ - public void onWakeUp(String reason, int reasonUid, String opPackageName, int opUid) { + public void onWakeUp(int reason, String details, int reasonUid, String opPackageName, + int opUid) { if (DEBUG) { - Slog.d(TAG, "onWakeUp: event=" + reason + ", reasonUid=" + reasonUid + Slog.d(TAG, "onWakeUp: reason=" + PowerManager.wakeReasonToString(reason) + + ", details=" + details + ", reasonUid=" + reasonUid + " opPackageName=" + opPackageName + " opUid=" + opUid); } try { - mBatteryStats.noteWakeUp(reason, reasonUid); + mBatteryStats.noteWakeUp(details, reasonUid); if (opPackageName != null) { mAppOps.noteOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 3ccd2341fbeb..1782b6a8aa78 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -39,7 +39,6 @@ import android.hardware.SystemSensorManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.V1_0.PowerHint; -import android.metrics.LogMaker; import android.net.Uri; import android.os.BatteryManager; import android.os.BatteryManagerInternal; @@ -52,6 +51,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.ServiceType; +import android.os.PowerManager.WakeReason; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; @@ -82,8 +82,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.internal.hardware.AmbientDisplayConfiguration; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.server.EventLogTags; @@ -199,7 +197,8 @@ public final class PowerManagerService extends SystemService // System Property indicating that retail demo mode is currently enabled. private static final String SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED = "sys.retaildemo.enabled"; - // Possible reasons for shutting down for use in data/misc/reboot/last_shutdown_reason + // Possible reasons for shutting down or reboot for use in REBOOT_PROPERTY(sys.boot.reason) + // which is set by bootstat private static final String REASON_SHUTDOWN = "shutdown"; private static final String REASON_REBOOT = "reboot"; private static final String REASON_USERREQUESTED = "shutdown,userrequested"; @@ -295,6 +294,7 @@ public final class PowerManagerService extends SystemService private long mLastSleepTime; // Last reason the device went to sleep. + private @WakeReason int mLastWakeReason; private int mLastSleepReason; // Timestamp of the last call to user activity. @@ -1119,8 +1119,9 @@ public final class PowerManagerService extends SystemService opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; } - wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid, - opPackageName, opUid); + wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, + opUid, opPackageName, opUid); } } @@ -1410,17 +1411,17 @@ public final class PowerManagerService extends SystemService } } - private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName, - int opUid) { + private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid, + String opPackageName, int opUid) { synchronized (mLock) { - if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) { + if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) { updatePowerStateLocked(); } } } - private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid, - String opPackageName, int opUid) { + private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details, + int reasonUid, String opPackageName, int opUid) { if (DEBUG_SPEW) { Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); } @@ -1434,25 +1435,18 @@ public final class PowerManagerService extends SystemService Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); try { - switch (mWakefulness) { - case WAKEFULNESS_ASLEEP: - Slog.i(TAG, "Waking up from sleep (uid=" + reasonUid + " reason=" + reason - + ")..."); - break; - case WAKEFULNESS_DREAMING: - Slog.i(TAG, "Waking up from dream (uid=" + reasonUid + " reason=" + reason - + ")..."); - break; - case WAKEFULNESS_DOZING: - Slog.i(TAG, "Waking up from dozing (uid=" + reasonUid + " reason=" + reason - + ")..."); - break; - } + Slog.i(TAG, "Waking up from " + + PowerManagerInternal.wakefulnessToString(mWakefulness) + + " (uid=" + reasonUid + + ", reason=" + PowerManager.wakeReasonToString(reason) + + ", details=" + details + + ")..."); mLastWakeTime = eventTime; - setWakefulnessLocked(WAKEFULNESS_AWAKE, 0); + mLastWakeReason = reason; + setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime); - mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid); + mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid); userActivityNoUpdateLocked( eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); } finally { @@ -1520,7 +1514,7 @@ public final class PowerManagerService extends SystemService mLastSleepTime = eventTime; mLastSleepReason = reason; mSandmanSummoned = true; - setWakefulnessLocked(WAKEFULNESS_DOZING, reason); + setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime); // Report the number of wake locks that will be cleared by going to sleep. int numWakeLocksCleared = 0; @@ -1570,7 +1564,7 @@ public final class PowerManagerService extends SystemService Slog.i(TAG, "Nap time (uid " + uid +")..."); mSandmanSummoned = true; - setWakefulnessLocked(WAKEFULNESS_DREAMING, 0); + setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -1593,7 +1587,8 @@ public final class PowerManagerService extends SystemService try { Slog.i(TAG, "Sleeping (uid " + uid +")..."); - setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + eventTime); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -1601,13 +1596,13 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - void setWakefulnessLocked(int wakefulness, int reason) { + void setWakefulnessLocked(int wakefulness, int reason, long eventTime) { if (mWakefulness != wakefulness) { mWakefulness = wakefulness; mWakefulnessChanging = true; mDirty |= DIRTY_WAKEFULNESS; if (mNotifier != null) { - mNotifier.onWakefulnessChangeStarted(wakefulness, reason); + mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime); } mAttentionDetector.onWakefulnessChangeStarted(wakefulness); } @@ -1631,23 +1626,6 @@ public final class PowerManagerService extends SystemService } } - private void logScreenOn() { - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - - final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime); - - LogMaker log = new LogMaker(MetricsEvent.SCREEN); - log.setType(MetricsEvent.TYPE_OPEN); - log.setSubtype(0); // not user initiated - log.setLatency(latencyMs); // How long it took. - MetricsLogger.action(log); - EventLogTags.writePowerScreenState(1, 0, 0, 0, latencyMs); - - if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { - Slog.w(TAG, "Screen on took " + latencyMs+ " ms"); - } - } - private void finishWakefulnessChangeIfNeededLocked() { if (mWakefulnessChanging && mDisplayReady) { if (mWakefulness == WAKEFULNESS_DOZING @@ -1658,7 +1636,11 @@ public final class PowerManagerService extends SystemService logSleepTimeoutRecapturedLocked(); } if (mWakefulness == WAKEFULNESS_AWAKE) { - logScreenOn(); + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); + final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime); + if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { + Slog.w(TAG, "Screen on took " + latencyMs + " ms"); + } } mWakefulnessChanging = false; mNotifier.onWakefulnessChangeFinished(); @@ -1786,7 +1768,8 @@ public final class PowerManagerService extends SystemService final long now = SystemClock.uptimeMillis(); if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType, dockedOnWirelessCharger)) { - wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID, + wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN, + "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); } userActivityNoUpdateLocked( @@ -2369,8 +2352,10 @@ public final class PowerManagerService extends SystemService PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); updatePowerStateLocked(); } else { - wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM", - Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); + wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_UNKNOWN, + "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID, + mContext.getOpPackageName(), Process.SYSTEM_UID); updatePowerStateLocked(); } } else if (wakefulness == WAKEFULNESS_DOZING) { @@ -4375,7 +4360,8 @@ public final class PowerManagerService extends SystemService } @Override // Binder call - public void wakeUp(long eventTime, String reason, String opPackageName) { + public void wakeUp(long eventTime, @WakeReason int reason, String details, + String opPackageName) { if (eventTime > SystemClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } @@ -4386,7 +4372,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - wakeUpInternal(eventTime, reason, uid, opPackageName, uid); + wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java index bd46a50f97de..fac95f9af42c 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java @@ -665,8 +665,7 @@ public class BatterySaverPolicy extends ContentObserver { config.getEnableAdjustBrightness(), config.getEnableDataSaver(), config.getEnableFirewall(), - // TODO: add option to config - config.getAdvertiseIsEnabled(), + config.getEnableNightMode(), config.getEnableQuickDoze(), /* filesForInteractive */ (new CpuFrequencies()).parseString(cpuFreqInteractive).toSysFileMap(), @@ -674,7 +673,7 @@ public class BatterySaverPolicy extends ContentObserver { (new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(), config.getForceAllAppsStandby(), config.getForceBackgroundCheck(), - config.getGpsMode() + config.getLocationMode() ); } diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java index 107cb2cc1a2e..4fb40db8597c 100644 --- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java +++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.role.IRoleManagerCallback; +import android.app.role.RoleManager; import android.app.role.RoleManagerCallback; import android.content.ComponentName; import android.content.Context; @@ -61,34 +62,35 @@ public class RemoteRoleControllerService { * Add a specific application to the holders of a role. If the role is exclusive, the previous * holder will be replaced. * - * @see RoleControllerService#onAddRoleHolder(String, String, RoleManagerCallback) + * @see RoleControllerService#onAddRoleHolder(String, String, int, RoleManagerCallback) */ public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, - @NonNull IRoleManagerCallback callback) { + @RoleManager.ManageHoldersFlags int flags, @NonNull IRoleManagerCallback callback) { mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) -> - service.onAddRoleHolder(roleName, packageName, callbackDelegate), callback)); + service.onAddRoleHolder(roleName, packageName, flags, callbackDelegate), callback)); } /** * Remove a specific application from the holders of a role. * - * @see RoleControllerService#onRemoveRoleHolder(String, String, RoleManagerCallback) + * @see RoleControllerService#onRemoveRoleHolder(String, String, int, RoleManagerCallback) */ public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, - @NonNull IRoleManagerCallback callback) { + @RoleManager.ManageHoldersFlags int flags, @NonNull IRoleManagerCallback callback) { mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) -> - service.onRemoveRoleHolder(roleName, packageName, callbackDelegate), callback)); + service.onRemoveRoleHolder(roleName, packageName, flags, callbackDelegate), + callback)); } /** * Remove all holders of a role. * - * @see RoleControllerService#onClearRoleHolders(String, RoleManagerCallback) + * @see RoleControllerService#onClearRoleHolders(String, int, RoleManagerCallback) */ public void onClearRoleHolders(@NonNull String roleName, - @NonNull IRoleManagerCallback callback) { + @RoleManager.ManageHoldersFlags int flags, @NonNull IRoleManagerCallback callback) { mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) -> - service.onClearRoleHolders(roleName, callbackDelegate), callback)); + service.onClearRoleHolders(roleName, flags, callbackDelegate), callback)); } /** diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 84305be0aed5..21bf9de5c8b0 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -201,10 +201,15 @@ public class RoleManagerService extends SystemService implements RoleUserState.C new ContentObserver(getContext().getMainThreadHandler()) { @Override public void onChange(boolean selfChange, Uri uri, int userId) { - getOrCreateControllerService(userId).onSmsKillSwitchToggled( - Settings.Global.getInt( - getContext().getContentResolver(), - Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1); + boolean killSwitchEnabled = Settings.Global.getInt( + getContext().getContentResolver(), + Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1; + for (int user : mUserManagerInternal.getUserIds()) { + if (mUserManagerInternal.isUserRunning(user)) { + getOrCreateControllerService(user) + .onSmsKillSwitchToggled(killSwitchEnabled); + } + } } }, UserHandle.USER_ALL); } @@ -450,7 +455,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C @Override public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @UserIdInt int userId, @NonNull IRoleManagerCallback callback) { + @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, + @NonNull IRoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); @@ -462,12 +468,14 @@ public class RoleManagerService extends SystemService implements RoleUserState.C getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "addRoleHolderAsUser"); - getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, callback); + getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, flags, + callback); } @Override public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @UserIdInt int userId, @NonNull IRoleManagerCallback callback) { + @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, + @NonNull IRoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); @@ -479,12 +487,13 @@ public class RoleManagerService extends SystemService implements RoleUserState.C getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "removeRoleHolderAsUser"); - getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName, + getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName, flags, callback); } @Override - public void clearRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId, + public void clearRoleHoldersAsUser(@NonNull String roleName, + @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @NonNull IRoleManagerCallback callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); @@ -496,7 +505,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "clearRoleHoldersAsUser"); - getOrCreateControllerService(userId).onClearRoleHolders(roleName, callback); + getOrCreateControllerService(userId).onClearRoleHolders(roleName, flags, callback); } @Override @@ -718,9 +727,9 @@ public class RoleManagerService extends SystemService implements RoleUserState.C }; if (packageName != null) { getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, - packageName, callback); + packageName, 0, callback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, + getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, callback); } try { diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java index b245e987022d..00021d7fb986 100644 --- a/services/core/java/com/android/server/role/RoleManagerShellCommand.java +++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java @@ -98,13 +98,22 @@ class RoleManagerShellCommand extends ShellCommand { return userId; } + private int getFlagsMaybe() { + String flags = getNextArg(); + if (flags == null) { + return 0; + } + return Integer.parseInt(flags); + } + private int runAddRoleHolder() throws RemoteException { int userId = getUserIdMaybe(); String roleName = getNextArgRequired(); String packageName = getNextArgRequired(); + int flags = getFlagsMaybe(); Callback callback = new Callback(); - mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback); + mRoleManager.addRoleHolderAsUser(roleName, packageName, flags, userId, callback); return callback.waitForResult(); } @@ -112,18 +121,20 @@ class RoleManagerShellCommand extends ShellCommand { int userId = getUserIdMaybe(); String roleName = getNextArgRequired(); String packageName = getNextArgRequired(); + int flags = getFlagsMaybe(); Callback callback = new Callback(); - mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback); + mRoleManager.removeRoleHolderAsUser(roleName, packageName, flags, userId, callback); return callback.waitForResult(); } private int runClearRoleHolders() throws RemoteException { int userId = getUserIdMaybe(); String roleName = getNextArgRequired(); + int flags = getFlagsMaybe(); Callback callback = new Callback(); - mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback); + mRoleManager.clearRoleHoldersAsUser(roleName, flags, userId, callback); return callback.waitForResult(); } @@ -134,9 +145,9 @@ class RoleManagerShellCommand extends ShellCommand { pw.println(" help"); pw.println(" Print this help text."); pw.println(); - pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE"); - pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE"); - pw.println(" clear-role-holders [--user USER_ID] ROLE"); + pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); + pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); + pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]"); pw.println(); } } diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java index 8dd076028b43..f3b838560ebd 100644 --- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java +++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java @@ -22,6 +22,7 @@ import android.content.rollback.RollbackInfo; import android.os.storage.StorageManager; import android.util.IntArray; import android.util.Log; +import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; @@ -51,11 +52,13 @@ public class AppDataRollbackHelper { * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers}, * a specified set of users for whom the package is installed. * - * @return a list of users for which the snapshot is pending, usually because data for one or - * more users is still credential locked. + * @return a {@link SnapshotAppDataResult}/ + * @see SnapshotAppDataResult */ - public IntArray snapshotAppData(String packageName, int[] installedUsers) { + public SnapshotAppDataResult snapshotAppData(String packageName, int[] installedUsers) { final IntArray pendingBackups = new IntArray(); + final SparseLongArray ceSnapshotInodes = new SparseLongArray(); + for (int user : installedUsers) { final int storageFlags; if (isUserCredentialLocked(user)) { @@ -69,14 +72,17 @@ public class AppDataRollbackHelper { } try { - mInstaller.snapshotAppData(packageName, user, storageFlags); + long ceSnapshotInode = mInstaller.snapshotAppData(packageName, user, storageFlags); + if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) { + ceSnapshotInodes.put(user, ceSnapshotInode); + } } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName + ", userId: " + user, ie); } } - return pendingBackups; + return new SnapshotAppDataResult(pendingBackups, ceSnapshotInodes); } /** @@ -138,6 +144,22 @@ public class AppDataRollbackHelper { } /** + * Deletes an app data data snapshot for a specified package {@code packageName} for a + * given {@code user}. + */ + public void destroyAppDataSnapshot(String packageName, int user, long ceSnapshotInode) { + int storageFlags = Installer.FLAG_STORAGE_DE; + if (ceSnapshotInode > 0) { + storageFlags |= Installer.FLAG_STORAGE_CE; + } + try { + mInstaller.destroyAppDataSnapshot(packageName, user, ceSnapshotInode, storageFlags); + } catch (InstallerException ie) { + Log.e(TAG, "Unable to delete app data snapshot for " + packageName, ie); + } + } + + /** * Computes the list of pending backups and restores for {@code userId} given lists of * available and recent rollbacks. Packages pending backup for the given user are added * to {@code pendingBackups} and packages pending restore are added to {@code pendingRestores} @@ -191,16 +213,28 @@ public class AppDataRollbackHelper { } /** - * Commits the list of pending backups and restores for a given {@code userId}. + * Commits the list of pending backups and restores for a given {@code userId}. For the pending + * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId} + * to a inode of theirs CE user data snapshot. */ public void commitPendingBackupAndRestoreForUser(int userId, - ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores) { + ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores, + List<RollbackData> changedRollbackData) { if (!pendingBackups.isEmpty()) { for (String packageName : pendingBackups) { try { - mInstaller.snapshotAppData(packageName, userId, Installer.FLAG_STORAGE_CE); + long ceSnapshotInode = mInstaller.snapshotAppData(packageName, userId, + Installer.FLAG_STORAGE_CE); + for (RollbackData data : changedRollbackData) { + for (PackageRollbackInfo info : data.packages) { + if (info.getPackageName().equals(packageName)) { + info.putCeSnapshotInode(userId, ceSnapshotInode); + } + } + } } catch (InstallerException ie) { - Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); + Log.e(TAG, "Unable to create app data snapshot for: " + packageName + + ", userId: " + userId, ie); } } } @@ -233,4 +267,26 @@ public class AppDataRollbackHelper { return StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(userId); } + + /** + * Encapsulates a result of {@link #snapshotAppData} method. + */ + public static final class SnapshotAppDataResult { + + /** + * A list of users for which the snapshot is pending, usually because data for one or more + * users is still credential locked. + */ + public final IntArray pendingBackups; + + /** + * A mapping between user and an inode of theirs CE data snapshot. + */ + public final SparseLongArray ceSnapshotInodes; + + public SnapshotAppDataResult(IntArray pendingBackups, SparseLongArray ceSnapshotInodes) { + this.pendingBackups = pendingBackups; + this.ceSnapshotInodes = ceSnapshotInodes; + } + } } diff --git a/services/core/java/com/android/server/rollback/RollbackData.java b/services/core/java/com/android/server/rollback/RollbackData.java index f45666574ed7..fcd5297f1363 100644 --- a/services/core/java/com/android/server/rollback/RollbackData.java +++ b/services/core/java/com/android/server/rollback/RollbackData.java @@ -64,6 +64,11 @@ class RollbackData { public boolean isAvailable; /** + * The id of the post-reboot apk session for a staged install, if any. + */ + public int apkSessionId = -1; + + /** * Whether this Rollback is currently in progress. This field is true from the point * we commit a {@code PackageInstaller} session containing these packages to the point the * {@code PackageInstaller} calls into the {@code onFinished} callback. diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java index ba6cdddb4cc8..f7ba9bbc695f 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerService.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java @@ -44,4 +44,11 @@ public final class RollbackManagerService extends SystemService { public void onUnlockUser(int user) { mService.onUnlockUser(user); } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_BOOT_COMPLETED) { + mService.onBootCompleted(); + } + } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 438e47964c5e..95c3f4c43313 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -43,6 +43,7 @@ import android.os.Process; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; +import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; @@ -110,7 +111,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private final HandlerThread mHandlerThread; private final Installer mInstaller; private final RollbackPackageHealthObserver mPackageHealthObserver; - private final AppDataRollbackHelper mUserdataHelper; + private final AppDataRollbackHelper mAppDataRollbackHelper; RollbackManagerServiceImpl(Context context) { mContext = context; @@ -124,7 +125,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback")); mPackageHealthObserver = new RollbackPackageHealthObserver(mContext); - mUserdataHelper = new AppDataRollbackHelper(mInstaller); + mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller); // Kick off loading of the rollback data from strorage in a background // thread. @@ -339,12 +340,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // for apex? if (!info.isApex()) { String installerPackageName = pm.getInstallerPackageName(info.getPackageName()); - if (installerPackageName == null) { - sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE, - "Cannot find installer package"); - return; + if (installerPackageName != null) { + params.setInstallerPackageName(installerPackageName); } - params.setInstallerPackageName(installerPackageName); } params.setAllowDowngrade(true); if (data.isStaged()) { @@ -449,7 +447,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (PackageRollbackInfo info : data.packages) { if (info.getPackageName().equals(packageName)) { iter.remove(); - mRollbackStore.deleteAvailableRollback(data); + deleteRollback(data); break; } } @@ -464,13 +462,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { final List<RollbackData> changed; synchronized (mLock) { ensureRollbackDataLoadedLocked(); - changed = mUserdataHelper.computePendingBackupsAndRestores(userId, + changed = mAppDataRollbackHelper.computePendingBackupsAndRestores(userId, pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks, mRecentlyExecutedRollbacks); } - mUserdataHelper.commitPendingBackupAndRestoreForUser(userId, - pendingBackupPackages, pendingRestorePackages); + mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, + pendingBackupPackages, pendingRestorePackages, changed); for (RollbackData rd : changed) { try { @@ -486,6 +484,47 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { }); } + void onBootCompleted() { + getHandler().post(() -> { + // Check to see if any staged sessions with rollback enabled have + // been applied. + List<RollbackData> staged = new ArrayList<>(); + synchronized (mLock) { + ensureRollbackDataLoadedLocked(); + for (RollbackData data : mAvailableRollbacks) { + if (data.stagedSessionId != -1) { + staged.add(data); + } + } + } + + for (RollbackData data : staged) { + PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); + PackageInstaller.SessionInfo session = installer.getSessionInfo( + data.stagedSessionId); + if (session != null) { + if (session.isSessionApplied()) { + synchronized (mLock) { + data.isAvailable = true; + } + try { + mRollbackStore.saveAvailableRollback(data); + } catch (IOException ioe) { + Log.e(TAG, "Unable to save rollback info for : " + + data.rollbackId, ioe); + } + } else if (session.isSessionFailed()) { + // TODO: Do we need to remove this from + // mAvailableRollbacks, or is it okay to leave as + // unavailable until the next reboot when it will go + // away on its own? + deleteRollback(data); + } + } + } + }); + } + /** * Load rollback data from storage if it has not already been loaded. * After calling this funciton, mAvailableRollbacks and @@ -551,7 +590,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { info.getVersionRolledBackFrom(), installedVersion)) { iter.remove(); - mRollbackStore.deleteAvailableRollback(data); + deleteRollback(data); break; } } @@ -664,7 +703,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) { iter.remove(); - mRollbackStore.deleteAvailableRollback(data); + deleteRollback(data); } else if (oldest == null || oldest.isAfter(data.timestamp)) { oldest = data.timestamp; } @@ -725,6 +764,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // ourselves. PackageInstaller.SessionInfo session = null; + int parentSessionId = -1; PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); for (PackageInstaller.SessionInfo info : installer.getAllSessions()) { if (info.isMultiPackage()) { @@ -732,12 +772,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageInstaller.SessionInfo child = installer.getSessionInfo(childId); if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) { // TODO: Check we only have one matching session? + parentSessionId = info.getSessionId(); session = child; break; } } } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) { // TODO: Check we only have one matching session? + parentSessionId = info.getSessionId(); session = info; break; } @@ -748,6 +790,58 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } + // Check to see if this is the apk session for a staged session with + // rollback enabled. + // TODO: This check could be made more efficient. + RollbackData rd = null; + synchronized (mLock) { + ensureRollbackDataLoadedLocked(); + for (int i = 0; i < mAvailableRollbacks.size(); ++i) { + RollbackData data = mAvailableRollbacks.get(i); + if (data.apkSessionId == parentSessionId) { + rd = data; + break; + } + } + } + + if (rd != null) { + // This is the apk session for a staged session. We have already + // backed up the apks, we just need to do user data backup. + PackageParser.PackageLite newPackage = null; + try { + newPackage = PackageParser.parsePackageLite( + new File(session.resolvedBaseCodePath), 0); + } catch (PackageParser.PackageParserException e) { + Log.e(TAG, "Unable to parse new package", e); + return false; + } + String packageName = newPackage.packageName; + for (PackageRollbackInfo info : rd.packages) { + if (info.getPackageName().equals(packageName)) { + AppDataRollbackHelper.SnapshotAppDataResult rs = + mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers); + info.getPendingBackups().addAll(rs.pendingBackups); + for (int i = 0; i < rs.ceSnapshotInodes.size(); i++) { + info.putCeSnapshotInode(rs.ceSnapshotInodes.keyAt(i), + rs.ceSnapshotInodes.valueAt(i)); + } + try { + mRollbackStore.saveAvailableRollback(rd); + } catch (IOException ioe) { + // TODO: Hopefully this is okay because we will try + // again to save the rollback when the staged session + // is applied. Just so long as the device doesn't + // reboot before then. + Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); + } + return true; + } + } + Log.e(TAG, "Unable to find package in apk session"); + return false; + } + return enableRollbackForSession(session, installedUsers, true); } @@ -800,13 +894,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, pkgInfo.getLongVersionCode()); - IntArray pendingBackups = IntArray.wrap(new int[0]); + final AppDataRollbackHelper.SnapshotAppDataResult result; if (snapshotUserData && !isApex) { - pendingBackups = mUserdataHelper.snapshotAppData(packageName, installedUsers); + result = mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers); + } else { + result = new AppDataRollbackHelper.SnapshotAppDataResult(IntArray.wrap(new int[0]), + new SparseLongArray()); } PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, - pendingBackups, new ArrayList<>(), isApex); + result.pendingBackups, new ArrayList<>(), isApex, IntArray.wrap(installedUsers), + result.ceSnapshotInodes); + RollbackData data; try { int childSessionId = session.getSessionId(); @@ -856,9 +955,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { getHandler().post(() -> { final RollbackData rollbackData = getRollbackForPackage(packageName); for (int userId : userIds) { - final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName, - rollbackData, userId, appId, ceDataInode, seInfo); - + final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData( + packageName, rollbackData, userId, appId, ceDataInode, seInfo); // We've updated metadata about this rollback, so save it to flash. if (changedRollbackData) { try { @@ -928,6 +1026,32 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } + @Override + public void notifyStagedApkSession(int originalSessionId, int apkSessionId) { + getHandler().post(() -> { + RollbackData rd = null; + synchronized (mLock) { + ensureRollbackDataLoadedLocked(); + for (int i = 0; i < mAvailableRollbacks.size(); ++i) { + RollbackData data = mAvailableRollbacks.get(i); + if (data.stagedSessionId == originalSessionId) { + data.apkSessionId = apkSessionId; + rd = data; + break; + } + } + } + + if (rd != null) { + try { + mRollbackStore.saveAvailableRollback(rd); + } catch (IOException ioe) { + Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); + } + } + }); + } + /** * Gets the version of the package currently installed. * Returns null if the package is not currently installed. @@ -1024,12 +1148,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); } catch (IOException e) { Log.e(TAG, "Unable to enable rollback", e); - mRollbackStore.deleteAvailableRollback(data); + deleteRollback(data); } } else { // The install session was aborted, clean up the pending // install. - mRollbackStore.deleteAvailableRollback(data); + deleteRollback(data); } } } @@ -1128,4 +1252,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { throw new IOException("Failed to allocate rollback ID"); } + + private void deleteRollback(RollbackData rollbackData) { + for (PackageRollbackInfo info : rollbackData.packages) { + IntArray installedUsers = info.getInstalledUsers(); + SparseLongArray ceSnapshotInodes = info.getCeSnapshotInodes(); + for (int i = 0; i < installedUsers.size(); i++) { + int userId = installedUsers.get(i); + mAppDataRollbackHelper.destroyAppDataSnapshot(info.getPackageName(), userId, + ceSnapshotInodes.get(userId, 0)); + } + } + mRollbackStore.deleteAvailableRollback(rollbackData); + } } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 3c6a54af4bd7..3a2b69f8a6e1 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -16,8 +16,11 @@ package com.android.server.rollback; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; @@ -25,6 +28,7 @@ import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Handler; import android.os.HandlerThread; +import android.os.PowerManager; import android.text.TextUtils; import android.util.Pair; import android.util.Slog; @@ -91,6 +95,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve + failedPackage.getVersionCode() + "]"); return false; } + RollbackInfo rollback = rollbackPair.first; // We only log mainline package rollbacks, so check if rollback contains the // module metadata provider, if it does, the rollback is a mainline rollback @@ -111,6 +116,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); + if (rollback.isStaged()) { + int rollbackId = rollback.getRollbackId(); + BroadcastReceiver listener = + listenForStagedSessionReady(rollbackManager, rollbackId); + handleStagedSessionChange(rollbackManager, rollbackId, listener); + } } else { StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, @@ -178,4 +189,42 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return null; } } + + private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager, + int rollbackId) { + BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + handleStagedSessionChange(rollbackManager, + rollbackId, this /* BroadcastReceiver */); + } + }; + IntentFilter sessionUpdatedFilter = + new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED); + mContext.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter); + return sessionUpdatedReceiver; + } + + private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId, + BroadcastReceiver listener) { + PackageInstaller packageInstaller = + mContext.getPackageManager().getPackageInstaller(); + List<RollbackInfo> recentRollbacks = + rollbackManager.getRecentlyCommittedRollbacks(); + for (int i = 0; i < recentRollbacks.size(); i++) { + RollbackInfo recentRollback = recentRollbacks.get(i); + int sessionId = recentRollback.getCommittedSessionId(); + if ((rollbackId == recentRollback.getRollbackId()) + && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) { + PackageInstaller.SessionInfo sessionInfo = + packageInstaller.getSessionInfo(sessionId); + if (sessionInfo.isSessionReady()) { + mContext.unregisterReceiver(listener); + mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); + } else if (sessionInfo.isSessionFailed()) { + mContext.unregisterReceiver(listener); + } + } + } + } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 1e45ba2e495c..be904eacebff 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -23,6 +23,7 @@ import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.util.IntArray; import android.util.Log; +import android.util.SparseLongArray; import libcore.io.IoUtils; @@ -160,6 +161,28 @@ class RollbackStore { return restoreInfos; } + private static @NonNull JSONArray ceSnapshotInodesToJson( + @NonNull SparseLongArray ceSnapshotInodes) throws JSONException { + JSONArray array = new JSONArray(); + for (int i = 0; i < ceSnapshotInodes.size(); i++) { + JSONObject entryJson = new JSONObject(); + entryJson.put("userId", ceSnapshotInodes.keyAt(i)); + entryJson.put("ceSnapshotInode", ceSnapshotInodes.valueAt(i)); + array.put(entryJson); + } + return array; + } + + private static @NonNull SparseLongArray ceSnapshotInodesFromJson(JSONArray json) + throws JSONException { + SparseLongArray ceSnapshotInodes = new SparseLongArray(json.length()); + for (int i = 0; i < json.length(); i++) { + JSONObject entry = json.getJSONObject(i); + ceSnapshotInodes.append(entry.getInt("userId"), entry.getLong("ceSnapshotInode")); + } + return ceSnapshotInodes; + } + /** * Reads the list of recently executed rollbacks from persistent storage. */ @@ -248,6 +271,7 @@ class RollbackStore { dataJson.put("timestamp", data.timestamp.toString()); dataJson.put("stagedSessionId", data.stagedSessionId); dataJson.put("isAvailable", data.isAvailable); + dataJson.put("apkSessionId", data.apkSessionId); PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json")); pw.println(dataJson.toString()); @@ -262,8 +286,6 @@ class RollbackStore { * rollback. */ void deleteAvailableRollback(RollbackData data) { - // TODO(narayan): Make sure we delete the userdata snapshot along with the backup of the - // actual app. removeFile(data.backupDir); } @@ -313,6 +335,7 @@ class RollbackStore { stagedSessionId, isAvailable); data.packages.addAll(packageRollbackInfosFromJson(dataJson.getJSONArray("packages"))); data.timestamp = Instant.parse(dataJson.getString("timestamp")); + data.apkSessionId = dataJson.getInt("apkSessionId"); return data; } catch (JSONException | DateTimeParseException e) { throw new IOException(e); @@ -339,11 +362,15 @@ class RollbackStore { IntArray pendingBackups = info.getPendingBackups(); List<RestoreInfo> pendingRestores = info.getPendingRestores(); + IntArray installedUsers = info.getInstalledUsers(); json.put("pendingBackups", convertToJsonArray(pendingBackups)); json.put("pendingRestores", convertToJsonArray(pendingRestores)); json.put("isApex", info.isApex()); + json.put("installedUsers", convertToJsonArray(installedUsers)); + json.put("ceSnapshotInodes", ceSnapshotInodesToJson(info.getCeSnapshotInodes())); + return json; } @@ -360,8 +387,12 @@ class RollbackStore { final boolean isApex = json.getBoolean("isApex"); + final IntArray installedUsers = convertToIntArray(json.getJSONArray("installedUsers")); + final SparseLongArray ceSnapshotInodes = ceSnapshotInodesFromJson( + json.getJSONArray("ceSnapshotInodes")); + return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo, - pendingBackups, pendingRestores, isApex); + pendingBackups, pendingRestores, isApex, installedUsers, ceSnapshotInodes); } private JSONArray versionedPackagesToJson(List<VersionedPackage> packages) diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 35867728f2c9..dd2cda20e0f1 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -45,6 +45,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.net.ConnectivityManager; import android.net.INetworkStatsService; @@ -1419,23 +1421,35 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private void pullNumFingerprints(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { + private void pullNumBiometricsEnrolled(int modality, int tagId, long elapsedNanos, + long wallClockNanos, List<StatsLogEventWrapper> pulledData) { FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class); - if (fingerprintManager == null) { + FaceManager faceManager = mContext.getSystemService(FaceManager.class); + if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) { + return; + } + if (modality == BiometricsProtoEnums.MODALITY_FACE && faceManager == null) { return; } UserManager userManager = mContext.getSystemService(UserManager.class); if (userManager == null) { return; } + final long token = Binder.clearCallingIdentity(); for (UserInfo user : userManager.getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int numFingerprints = fingerprintManager.getEnrolledFingerprints(userId).size(); + int numEnrolled = 0; + if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) { + numEnrolled = fingerprintManager.getEnrolledFingerprints(userId).size(); + } else if (modality == BiometricsProtoEnums.MODALITY_FACE) { + numEnrolled = faceManager.getEnrolledFaces(userId).size(); + } else { + return; + } StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); e.writeInt(userId); - e.writeInt(numFingerprints); + e.writeInt(numEnrolled); pulledData.add(e); } Binder.restoreCallingIdentity(token); @@ -2027,7 +2041,13 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { break; } case StatsLog.NUM_FINGERPRINTS_ENROLLED: { - pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret); + pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FINGERPRINT, tagId, + elapsedNanos, wallClockNanos, ret); + break; + } + case StatsLog.NUM_FACES_ENROLLED: { + pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FACE, tagId, elapsedNanos, + wallClockNanos, ret); break; } case StatsLog.PROC_STATS: { diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java index 4adce586e9a5..ec62ec76b32f 100644 --- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java +++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java @@ -22,6 +22,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; +import android.debug.AdbManagerInternal; import android.os.BatteryManager; import android.os.Binder; import android.os.IBinder; @@ -42,6 +43,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; @@ -49,7 +51,6 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.util.Set; @@ -85,6 +86,7 @@ public class TestHarnessModeService extends SystemService { break; case PHASE_BOOT_COMPLETED: disableAutoSync(); + configureSettings(); break; } super.onBootPhase(phase); @@ -98,47 +100,60 @@ public class TestHarnessModeService extends SystemService { return; } mShouldSetUpTestHarnessMode = true; + setUpAdb(testHarnessModeData); + setDeviceProvisioned(); + } + + private void setUpAdb(byte[] testHarnessModeData) { + ContentResolver cr = getContext().getContentResolver(); + // Disable the TTL for ADB keys before enabling ADB + Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0); + PersistentData persistentData = PersistentData.fromBytes(testHarnessModeData); SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, persistentData.mEnabled ? "1" : "0"); writeAdbKeysFile(persistentData); - // Clear out the data block so that we don't revert the ADB keys on every boot. - getPersistentDataBlock().clearTestHarnessModeData(); + } - ContentResolver cr = getContext().getContentResolver(); - if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) { - // Enable ADB - Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1); - } else { - // ADB is already enabled, we should restart the service so it picks up the new keys - android.os.SystemService.restart("adbd"); + private void disableAutoSync() { + if (!mShouldSetUpTestHarnessMode) { + return; } + UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser(); + ContentResolver + .setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier()); + } + + private void configureSettings() { + if (!mShouldSetUpTestHarnessMode) { + return; + } + ContentResolver cr = getContext().getContentResolver(); + Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1); + Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0); Settings.Global.putInt( cr, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_ANY); Settings.Global.putInt(cr, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, 1); - Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); - - setDeviceProvisioned(); } - private void disableAutoSync() { - if (!mShouldSetUpTestHarnessMode) { - return; - } - UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser(); - ContentResolver - .setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier()); + private void writeAdbKeysFile(PersistentData persistentData) { + AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class); + + writeBytesToFile(persistentData.mAdbKeys, adbManager.getAdbKeysFile().toPath()); + writeBytesToFile(persistentData.mAdbTempKeys, adbManager.getAdbTempKeysFile().toPath()); + + // Clear out the data block so that we don't revert the ADB keys on every boot. + getPersistentDataBlock().clearTestHarnessModeData(); } - private void writeAdbKeysFile(PersistentData persistentData) { - Path adbKeys = Paths.get("/data/misc/adb/adb_keys"); + private void writeBytesToFile(byte[] keys, Path adbKeys) { try { OutputStream fileOutputStream = Files.newOutputStream(adbKeys); - fileOutputStream.write(persistentData.mAdbKeys); + fileOutputStream.write(keys); fileOutputStream.close(); Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(adbKeys); @@ -219,23 +234,22 @@ public class TestHarnessModeService extends SystemService { } private int handleEnable() { - Path adbKeys = Paths.get("/data/misc/adb/adb_keys"); - if (!Files.exists(adbKeys)) { + AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class); + File adbKeys = adbManager.getAdbKeysFile(); + File adbTempKeys = adbManager.getAdbTempKeysFile(); + if (adbKeys == null && adbTempKeys == null) { // This should only be accessible on eng builds that haven't yet set up ADB keys getErrPrintWriter() .println("No ADB keys stored; not enabling test harness mode"); return 1; } - try (InputStream inputStream = Files.newInputStream(adbKeys)) { - long size = Files.size(adbKeys); - byte[] adbKeysBytes = new byte[(int) size]; - int numBytes = inputStream.read(adbKeysBytes); - if (numBytes != size) { - getErrPrintWriter().println("Failed to read all bytes of adb_keys"); - return 1; - } - PersistentData persistentData = new PersistentData(true, adbKeysBytes); + try { + byte[] adbKeysBytes = getBytesFromFile(adbKeys); + byte[] adbTempKeysBytes = getBytesFromFile(adbTempKeys); + + PersistentData persistentData = + new PersistentData(true, adbKeysBytes, adbTempKeysBytes); getPersistentDataBlock().setTestHarnessModeData(persistentData.toBytes()); } catch (IOException e) { Slog.e(TAG, "Failed to store ADB keys.", e); @@ -252,6 +266,22 @@ public class TestHarnessModeService extends SystemService { return 0; } + private byte[] getBytesFromFile(File file) throws IOException { + if (file == null || !file.exists()) { + return new byte[0]; + } + Path path = file.toPath(); + try (InputStream inputStream = Files.newInputStream(path)) { + int size = (int) Files.size(path); + byte[] bytes = new byte[size]; + int numBytes = inputStream.read(bytes); + if (numBytes != size) { + throw new IOException("Failed to read the whole file"); + } + return bytes; + } + } + @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -290,15 +320,17 @@ public class TestHarnessModeService extends SystemService { final int mVersion; final boolean mEnabled; final byte[] mAdbKeys; + final byte[] mAdbTempKeys; - PersistentData(boolean enabled, byte[] adbKeys) { - this(VERSION_1, enabled, adbKeys); + PersistentData(boolean enabled, byte[] adbKeys, byte[] adbTempKeys) { + this(VERSION_1, enabled, adbKeys, adbTempKeys); } - PersistentData(int version, boolean enabled, byte[] adbKeys) { + PersistentData(int version, boolean enabled, byte[] adbKeys, byte[] adbTempKeys) { this.mVersion = version; this.mEnabled = enabled; this.mAdbKeys = adbKeys; + this.mAdbTempKeys = adbTempKeys; } static PersistentData fromBytes(byte[] bytes) { @@ -309,7 +341,10 @@ public class TestHarnessModeService extends SystemService { int adbKeysLength = is.readInt(); byte[] adbKeys = new byte[adbKeysLength]; is.readFully(adbKeys); - return new PersistentData(version, enabled, adbKeys); + int adbTempKeysLength = is.readInt(); + byte[] adbTempKeys = new byte[adbTempKeysLength]; + is.readFully(adbTempKeys); + return new PersistentData(version, enabled, adbKeys, adbTempKeys); } catch (IOException e) { throw new RuntimeException(e); } @@ -323,6 +358,8 @@ public class TestHarnessModeService extends SystemService { dos.writeBoolean(mEnabled); dos.writeInt(mAdbKeys.length); dos.write(mAdbKeys); + dos.writeInt(mAdbTempKeys.length); + dos.write(mAdbTempKeys); dos.close(); return os.toByteArray(); } catch (IOException e) { diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 1163d3916cb1..057b53e6232f 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -128,6 +128,8 @@ public final class TvInputManagerService extends SystemService { private final WatchLogHandler mWatchLogHandler; + private IBinder.DeathRecipient mDeathRecipient; + public TvInputManagerService(Context context) { super(context); @@ -674,6 +676,7 @@ public final class TvInputManagerService extends SystemService { if (sessionToken == userState.mainSessionToken) { setMainLocked(sessionToken, false, callingUid, userId); } + sessionState.session.asBinder().unlinkToDeath(sessionState, 0); sessionState.session.release(); } } catch (RemoteException | SessionNotFoundException e) { @@ -709,6 +712,7 @@ public final class TvInputManagerService extends SystemService { clientState.sessionTokens.remove(sessionToken); if (clientState.isEmpty()) { userState.clientStateMap.remove(sessionState.client.asBinder()); + sessionState.client.asBinder().unlinkToDeath(clientState, 0); } } @@ -1002,17 +1006,19 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { final UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.callbackSet.add(callback); - try { - callback.asBinder().linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - synchronized (mLock) { - if (userState.callbackSet != null) { - userState.callbackSet.remove(callback); - } + mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mLock) { + if (userState.callbackSet != null) { + userState.callbackSet.remove(callback); } } - }, 0); + } + }; + + try { + callback.asBinder().linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { Slog.e(TAG, "client process has already died", e); } @@ -1031,6 +1037,7 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.callbackSet.remove(callback); + callback.asBinder().unlinkToDeath(mDeathRecipient, 0); } } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 744efab7d78d..332df956d0fb 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -1067,8 +1067,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { // Figure out the value returned when access is allowed final int allowedResult; - if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) { - // If we're extending a persistable grant, then we need to return + if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0 + || pi.forceUriPermissions) { + // If we're extending a persistable grant or need to force, then we need to return // "targetUid" so that we always create a grant data structure to // support take/release APIs allowedResult = targetUid; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 071dde74f103..b0ef8a0d4209 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2243,9 +2243,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub synchronized (mLock) { mInAmbientMode = inAmbientMode; final WallpaperData data = mWallpaperMap.get(mCurrentUserId); + final boolean hasConnection = data != null && data.connection != null; + final WallpaperInfo info = hasConnection ? data.connection.mInfo : null; + // The wallpaper info is null for image wallpaper, also use the engine in this case. - if (data != null && data.connection != null && (data.connection.mInfo == null - || data.connection.mInfo.supportsAmbientMode())) { + if (hasConnection && (info == null && isAodImageWallpaperEnabled() + || info != null && info.supportsAmbientMode())) { // TODO(multi-display) Extends this method with specific display. engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; } else { diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index 62421ac4fc76..f882fdee8c1f 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -40,6 +40,7 @@ import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY; import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE; import static com.android.server.am.ActivityDisplayProto.STACKS; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; @@ -575,7 +576,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> final ActivityStack stack = mStacks.get(stackNdx); final ActivityRecord resumedActivity = stack.getResumedActivity(); if (resumedActivity != null - && (!stack.shouldBeVisible(resuming) || !stack.isFocusable())) { + && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE + || !stack.isFocusable())) { if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack + " mResumedActivity=" + resumedActivity); someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index ef4064846381..4faf910f52ea 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -109,6 +109,7 @@ import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; import static com.android.server.wm.ActivityStack.LAUNCH_TICK; import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG; import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS; @@ -2054,8 +2055,10 @@ final class ActivityRecord extends ConfigurationContainer { * @param activeActivity the activity that is active or just completed pause action. We won't * resume if this activity is active. */ - private boolean shouldResumeActivity(ActivityRecord activeActivity) { - return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED); + @VisibleForTesting + boolean shouldResumeActivity(ActivityRecord activeActivity) { + return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED) + && getActivityStack().getVisibility(activeActivity) == STACK_VISIBILITY_VISIBLE; } /** diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 6f07be82d267..cc78588eeb84 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -108,6 +108,7 @@ import static com.android.server.wm.RootActivityContainer.FindTaskResult; import static java.lang.Integer.MAX_VALUE; +import android.annotation.IntDef; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -223,6 +224,22 @@ class ActivityStack extends ConfigurationContainer { // How many activities have to be scheduled to stop to force a stop pass. private static final int MAX_STOPPING_TO_FORCE = 3; + @IntDef(prefix = {"STACK_VISIBILITY"}, value = { + STACK_VISIBILITY_VISIBLE, + STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + STACK_VISIBILITY_INVISIBLE, + }) + @interface StackVisibility {} + + /** Stack is visible. No other stacks on top that fully or partially occlude it. */ + static final int STACK_VISIBILITY_VISIBLE = 0; + + /** Stack is partially occluded by other translucent stack(s) on top of it. */ + static final int STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1; + + /** Stack is completely invisible. */ + static final int STACK_VISIBILITY_INVISIBLE = 2; + @Override protected int getChildCount() { return mTaskHistory.size(); @@ -1959,14 +1976,28 @@ class ActivityStack extends ConfigurationContainer { * @param starting The currently starting activity or null if there is none. */ boolean shouldBeVisible(ActivityRecord starting) { + return getVisibility(starting) != STACK_VISIBILITY_INVISIBLE; + } + + /** + * Returns true if the stack should be visible. + * + * @param starting The currently starting activity or null if there is none. + */ + @StackVisibility + int getVisibility(ActivityRecord starting) { if (!isAttached() || mForceHidden) { - return false; + return STACK_VISIBILITY_INVISIBLE; } final ActivityDisplay display = getDisplay(); boolean gotSplitScreenStack = false; boolean gotOpaqueSplitScreenPrimary = false; boolean gotOpaqueSplitScreenSecondary = false; + boolean gotTranslucentFullscreen = false; + boolean gotTranslucentSplitScreenPrimary = false; + boolean gotTranslucentSplitScreenSecondary = false; + boolean shouldBeVisible = true; final int windowingMode = getWindowingMode(); final boolean isAssistantType = isActivityTypeAssistant(); for (int i = display.getChildCount() - 1; i >= 0; --i) { @@ -1975,8 +2006,9 @@ class ActivityStack extends ConfigurationContainer { if (other == this) { // Should be visible if there is no other stack occluding it, unless it doesn't // have any running activities, not starting one and not home stack. - return hasRunningActivities || isInStackLocked(starting) != null + shouldBeVisible = hasRunningActivities || isInStackLocked(starting) != null || isActivityTypeHome(); + break; } if (!hasRunningActivities) { @@ -1996,51 +2028,79 @@ class ActivityStack extends ConfigurationContainer { if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { if (activityType == ACTIVITY_TYPE_HOME || (activityType == ACTIVITY_TYPE_ASSISTANT - && mWindowManager.getRecentsAnimationController() != null)) { - return true; + && mWindowManager.getRecentsAnimationController() != null)) { + break; } } if (other.isStackTranslucent(starting)) { // Can be visible behind a translucent fullscreen stack. + gotTranslucentFullscreen = true; continue; } - return false; + return STACK_VISIBILITY_INVISIBLE; } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !gotOpaqueSplitScreenPrimary) { gotSplitScreenStack = true; - gotOpaqueSplitScreenPrimary = - !other.isStackTranslucent(starting); + gotTranslucentSplitScreenPrimary = other.isStackTranslucent(starting); + gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary; if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && gotOpaqueSplitScreenPrimary) { // Can not be visible behind another opaque stack in split-screen-primary mode. - return false; + return STACK_VISIBILITY_INVISIBLE; } } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY && !gotOpaqueSplitScreenSecondary) { gotSplitScreenStack = true; - gotOpaqueSplitScreenSecondary = - !other.isStackTranslucent(starting); + gotTranslucentSplitScreenSecondary = other.isStackTranslucent(starting); + gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary; if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY && gotOpaqueSplitScreenSecondary) { // Can not be visible behind another opaque stack in split-screen-secondary mode. - return false; + return STACK_VISIBILITY_INVISIBLE; } } if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) { // Can not be visible if we are in split-screen windowing mode and both halves of // the screen are opaque. - return false; + return STACK_VISIBILITY_INVISIBLE; } if (isAssistantType && gotSplitScreenStack) { // Assistant stack can't be visible behind split-screen. In addition to this not // making sense, it also works around an issue here we boost the z-order of the // assistant window surfaces in window manager whenever it is visible. - return false; + return STACK_VISIBILITY_INVISIBLE; } } - // Well, nothing is stopping you from being visible... - return true; + if (!shouldBeVisible) { + return STACK_VISIBILITY_INVISIBLE; + } + + // Handle cases when there can be a translucent split-screen stack on top. + switch (windowingMode) { + case WINDOWING_MODE_FULLSCREEN: + if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) { + // At least one of the split-screen stacks that covers this one is translucent. + return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; + } + break; + case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: + if (gotTranslucentSplitScreenPrimary) { + // Covered by translucent primary split-screen on top. + return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; + } + break; + case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: + if (gotTranslucentSplitScreenSecondary) { + // Covered by translucent secondary split-screen on top. + return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; + } + break; + } + + // Lastly - check if there is a translucent fullscreen stack on top. + return gotTranslucentFullscreen ? STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT + : STACK_VISIBILITY_VISIBLE; } final int rankTaskLayers(int baseLayer) { diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index c0fe6e937422..0a3c2fba3930 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -97,7 +97,6 @@ import android.app.servertransaction.LaunchActivityItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -467,7 +466,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { * initialized. So we initialize our wakelocks afterwards. */ void initPowerManagement() { - mPowerManager = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE); + mPowerManager = mService.mContext.getSystemService(PowerManager.class); mGoingToSleep = mPowerManager .newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep"); mLaunchingActivity = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*"); @@ -2456,7 +2455,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } void wakeUp(String reason) { - mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason); + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, + "android.server.am:TURN_ON:" + reason); } /** diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 1d71d8764bf7..678a896bddbc 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -753,8 +753,8 @@ class ActivityStarter { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shouldAbortBackgroundActivityStart"); abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, realCallingUid, callerApp, originatingPendingIntent, - allowBackgroundActivityStart, intent); + callingPackage, realCallingUid, realCallingPid, callerApp, + originatingPendingIntent, allowBackgroundActivityStart, intent); } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -914,22 +914,14 @@ class ActivityStarter { } private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, - final String callingPackage, int realCallingUid, WindowProcessController callerApp, - PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart, - Intent intent) { + final String callingPackage, int realCallingUid, int realCallingPid, + WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, + boolean allowBackgroundActivityStart, Intent intent) { // don't abort for the most important UIDs if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID || callingUid == Process.NFC_UID) { return false; } - // don't abort if the callerApp has any visible activity - if (callerApp != null && callerApp.hasForegroundActivities()) { - return false; - } - // don't abort if the callerApp is instrumenting with background activity starts privileges - if (callerApp != null && callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) { - return false; - } // don't abort if the callingUid is in the foreground or is a persistent system process final int callingUidProcState = mService.getUidStateLocked(callingUid); final boolean callingUidHasAnyVisibleWindow = @@ -967,9 +959,26 @@ class ActivityStarter { return false; } } - // don't abort if the caller is currently temporarily whitelisted - if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) { - return false; + // If we don't have callerApp at this point, no caller was provided to startActivity(). + // That's the case for PendingIntent-based starts, since the creator's process might not be + // up and alive. If that's the case, we retrieve the WindowProcessController for the send() + // caller, so that we can make the decision based on its foreground/whitelisted state. + if (callerApp == null) { + callerApp = mService.getProcessController(realCallingPid, realCallingUid); + } + if (callerApp != null) { + // don't abort if the callerApp has any visible activity + if (callerApp.hasForegroundActivities()) { + return false; + } + // don't abort if the callerApp is instrumenting with background activity starts privs + if (callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) { + return false; + } + // don't abort if the caller is currently temporarily whitelisted + if (callerApp.areBackgroundActivityStartsAllowed()) { + return false; + } } // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) @@ -996,6 +1005,7 @@ class ActivityStarter { + "; originatingPendingIntent: " + originatingPendingIntent + "; isBgStartWhitelisted: " + allowBackgroundActivityStart + "; intent: " + intent + + "; callerApp: " + callerApp + "]"); // log aborted activity start to TRON if (mService.isActivityStartsLoggingEnabled()) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index b6bac6131986..875fc4eab273 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1383,6 +1383,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setProfilerInfo(profilerInfo) .setActivityOptions(bOptions) .setMayWait(userId) + .setAllowBackgroundActivityStart(true) .execute(); } @@ -1398,6 +1399,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setResolvedType(resolvedType) .setActivityOptions(bOptions) .setMayWait(userId) + .setAllowBackgroundActivityStart(true) .execute(); } @@ -5666,6 +5668,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return null; } + WindowProcessController getProcessController(int pid, int uid) { + final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap(); + for (int i = pmap.size()-1; i >= 0; i--) { + final SparseArray<WindowProcessController> procs = pmap.valueAt(i); + for (int j = procs.size() - 1; j >= 0; j--) { + final WindowProcessController proc = procs.valueAt(j); + if (UserHandle.isApp(uid) && proc.getPid() == pid && proc.mUid == uid) { + return proc; + } + } + } + return null; + } + int getUidStateLocked(int uid) { return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT); } diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index 6fcc331bf62f..5519729c17f5 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -143,7 +143,7 @@ class AppWindowThumbnail implements Animatable { void destroy() { mSurfaceAnimator.cancelAnimation(); - mSurfaceControl.destroy(); + getPendingTransaction().remove(mSurfaceControl); } /** diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 3246a8702cd1..bcf6abaac5da 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1275,7 +1275,19 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void onDisplayChanged(DisplayContent dc) { DisplayContent prevDc = mDisplayContent; super.onDisplayChanged(dc); - if (prevDc != null && prevDc.mFocusedApp == this) { + if (prevDc == null) { + return; + } + if (prevDc.mChangingApps.contains(this)) { + // This gets called *after* the AppWindowToken has been reparented to the new display. + // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN), + // so this token is now "frozen" while waiting for the animation to start on prevDc + // (which will be cancelled since the window is no-longer a child). However, since this + // is no longer a child of prevDc, this won't be notified of the cancelled animation, + // so we need to cancel the change transition here. + clearChangeLeash(getPendingTransaction(), true /* cancel */); + } + if (prevDc.mFocusedApp == this) { prevDc.setFocusedApp(null); final TaskStack stack = dc.getTopStack(); if (stack != null) { @@ -1584,7 +1596,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) { - if (!isVisible() || getDisplayContent().mAppTransition.isTransitionSet()) { + if (mWmService.mDisableTransitionAnimation + || !isVisible() + || getDisplayContent().mAppTransition.isTransitionSet() + || getSurfaceControl() == null) { return false; } // Only do an animation into and out-of freeform mode for now. Other mode @@ -2504,7 +2519,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree public void onAnimationLeashDestroyed(Transaction t) { super.onAnimationLeashDestroyed(t); if (mAnimationBoundsLayer != null) { - t.reparent(mAnimationBoundsLayer, null); + t.remove(mAnimationBoundsLayer); mAnimationBoundsLayer = null; } @@ -2561,9 +2576,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return; } else if (mTransitChangeLeash != null) { // unparent mTransitChangeLeash for clean-up - t.hide(mTransitChangeLeash); - t.reparent(mTransitChangeLeash, null); - mTransitChangeLeash = null; + clearChangeLeash(t, false /* cancel */); } if (mAnimatingAppWindowTokenRegistry != null) { @@ -2659,15 +2672,36 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return super.isSelfAnimating(); } + /** + * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring + * to another leash. + */ + private void clearChangeLeash(Transaction t, boolean cancel) { + if (mTransitChangeLeash == null) { + return; + } + if (cancel) { + clearThumbnail(); + SurfaceControl sc = getSurfaceControl(); + SurfaceControl parentSc = getParentSurfaceControl(); + // Don't reparent if surface is getting destroyed + if (parentSc != null && sc != null) { + t.reparent(sc, getParentSurfaceControl()); + } + } + t.hide(mTransitChangeLeash); + t.reparent(mTransitChangeLeash, null); + mTransitChangeLeash = null; + if (cancel) { + onAnimationLeashDestroyed(t); + } + } + @Override void cancelAnimation() { cancelAnimationOnly(); clearThumbnail(); - if (mTransitChangeLeash != null) { - getPendingTransaction().hide(mTransitChangeLeash); - getPendingTransaction().reparent(mTransitChangeLeash, null); - mTransitChangeLeash = null; - } + clearChangeLeash(getPendingTransaction(), true /* cancel */); } /** @@ -3003,7 +3037,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void removeFromPendingTransition() { if (isWaitingForTransitionStart() && mDisplayContent != null) { mDisplayContent.mOpeningApps.remove(this); - mDisplayContent.mChangingApps.remove(this); + if (mDisplayContent.mChangingApps.remove(this)) { + clearChangeLeash(getPendingTransaction(), true /* cancel */); + } mDisplayContent.mClosingApps.remove(this); } } diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java index c90f5bfb7ee0..497e4121f12e 100644 --- a/services/core/java/com/android/server/wm/BlackFrame.java +++ b/services/core/java/com/android/server/wm/BlackFrame.java @@ -153,7 +153,7 @@ public class BlackFrame { if (mBlackSurfaces[i] != null) { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM, " BLACK " + mBlackSurfaces[i].surface + ": DESTROY"); - mBlackSurfaces[i].surface.destroy(); + mBlackSurfaces[i].surface.remove(); mBlackSurfaces[i] = null; } } diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index c39060ee1922..1373e1879d14 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -129,7 +129,7 @@ class Dimmer { final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer); mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> { if (!mDimming) { - dimAnimatable.getPendingTransaction().reparent(mDimLayer, null); + dimAnimatable.getPendingTransaction().remove(mDimLayer); } }, mHost.mWmService); } @@ -300,7 +300,7 @@ class Dimmer { if (!mDimState.mDimming) { if (!mDimState.mAnimateExit) { - t.reparent(mDimState.mDimLayer, null); + t.remove(mDimState.mDimLayer); } else { startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 18df88b37c61..5cfc20b6339f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -33,14 +32,12 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; import static android.view.Display.INVALID_DISPLAY; import static android.view.InsetsState.TYPE_IME; -import static android.view.InsetsState.TYPE_NAVIGATION_BAR; -import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; @@ -141,6 +138,7 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; @@ -1162,14 +1160,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, ConfigurationContainer requestingContainer) { - final int previousRotation = mRotation; final Configuration config = updateOrientationFromAppTokens( getRequestedOverrideConfiguration(), freezeDisplayToken, false); - // This event is considered handled iff a configuration propagation is triggered, because - // that's the only place lower level containers check if they need to do something to this - // request. The only guaranteed signal is that the display is rotated to a different - // orientation (i.e. rotating 180 degrees doesn't count). - final boolean handled = (mRotation - previousRotation) % 2 != 0; + // If display rotation class tells us that it doesn't consider app requested orientation, + // this display won't rotate just because of an app changes its requested orientation. Thus + // it indicates that this display chooses not to handle this request. + final boolean handled = getDisplayRotation().respectAppRequestedOrientation(); if (config == null) { return handled; } @@ -1191,6 +1187,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return handled; } + @Override + boolean handlesOrientationChangeFromDescendant() { + return getDisplayRotation().respectAppRequestedOrientation(); + } + /** * Determine the new desired orientation of this display. * @@ -1371,8 +1372,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId + " selected orientation " + lastOrientation - + ", got rotation " + rotation + " which has " - + " metrics"); + + ", got rotation " + rotation); if (oldRotation == rotation) { // No change. @@ -3253,6 +3253,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodTargetWaitingAnim = targetWaitingAnim; assignWindowLayers(false /* setLayoutNeeded */); mInsetsStateController.onImeTargetChanged(target); + updateImeParent(); + } + + private void updateImeParent() { + if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE) { + return; + } + final SurfaceControl newParent = computeImeParent(); + if (newParent != null) { + mPendingTransaction.reparent(mImeWindowsContainers.mSurfaceControl, newParent); + scheduleAnimation(); + } + } + + /** + * Computes the window the IME should be attached to. + */ + @VisibleForTesting + SurfaceControl computeImeParent() { + + // Attach it to app if the target is part of an app and such app is covering the entire + // screen. If it's not covering the entire screen the IME might extend beyond the apps + // bounds. + if (mInputMethodTarget != null && mInputMethodTarget.mAppToken != null && + mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + return mInputMethodTarget.mAppToken.getSurfaceControl(); + } + + // Otherwise, we just attach it to the display. + return mWindowingLayer; } boolean getNeedsMenu(WindowState top, WindowManagerPolicy.WindowState bottom) { @@ -4420,13 +4450,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .show(mSplitScreenDividerAnchor); scheduleAnimation(); } else { - mAppAnimationLayer.destroy(); + mAppAnimationLayer.remove(); mAppAnimationLayer = null; - mBoostedAppAnimationLayer.destroy(); + mBoostedAppAnimationLayer.remove(); mBoostedAppAnimationLayer = null; - mHomeAppAnimationLayer.destroy(); + mHomeAppAnimationLayer.remove(); mHomeAppAnimationLayer = null; - mSplitScreenDividerAnchor.destroy(); + mSplitScreenDividerAnchor.remove(); mSplitScreenDividerAnchor = null; } } @@ -4887,6 +4917,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc); } + @VisibleForTesting + SurfaceControl getWindowingLayer() { + return mWindowingLayer; + } + /** * Create a portal window handle for input. This window transports any touch to the display * indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window. @@ -4911,4 +4946,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo portalWindowHandle.portalToDisplayId = mDisplayId; return portalWindowHandle; } + + /** + * @see IWindowManager#setForwardedInsets + */ + public void setForwardedInsets(Insets insets) { + if (insets == null) { + insets = Insets.NONE; + } + if (mDisplayPolicy.getForwardedInsets().equals(insets)) { + return; + } + mDisplayPolicy.setForwardedInsets(insets); + setLayoutNeeded(); + mWmService.mWindowPlacerLocked.requestTraversal(); + } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index bbf115f17969..2ee30ac5c8ff 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -27,6 +27,7 @@ import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; @@ -103,6 +104,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.localLOGV; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; @@ -110,6 +112,7 @@ import android.app.StatusBarManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.graphics.Insets; import android.graphics.Rect; import android.hardware.input.InputManager; import android.hardware.power.V1_0.PowerHint; @@ -134,6 +137,7 @@ import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.View; +import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; @@ -339,6 +343,16 @@ public class DisplayPolicy { private InputConsumer mInputConsumer = null; + /** + * The area covered by system windows which belong to another display. Forwarded insets is set + * in case this is a virtual display, this is displayed on another display that has insets, and + * the bounds of this display is overlapping with the insets of the host display (e.g. IME is + * displayed on the host display, and it covers a part of this virtual display.) + * The forwarded insets is used to compute display frames of this virtual display, which will + * be then used to layout windows in the virtual display. + */ + @NonNull private Insets mForwardedInsets = Insets.NONE; + // -------- PolicyHandler -------- private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1; private static final int MSG_REQUEST_TRANSIENT_BARS = 2; @@ -1362,6 +1376,15 @@ public class DisplayPolicy { displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, displayFrames.mStable.top); } + + // In case this is a virtual display, and the host display has insets that overlap this + // virtual display, apply the insets of the overlapped area onto the current and content + // frame of this virtual display. This let us layout windows in the virtual display as + // expected when the window needs to avoid overlap with the system windows. + // TODO: Generalize the forwarded insets, so that we can handle system windows other than + // IME. + displayFrames.mCurrent.inset(mForwardedInsets); + displayFrames.mContent.inset(mForwardedInsets); } private void layoutScreenDecorWindows(DisplayFrames displayFrames) { @@ -1900,7 +1923,10 @@ public class DisplayPolicy { if (win.isVoiceInteraction()) { cf.set(displayFrames.mVoiceContent); } else { - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + // IME Insets are handled on the client for ADJUST_RESIZE in the new + // insets world + if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE + || adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.set(displayFrames.mDock); } else { cf.set(displayFrames.mContent); @@ -1991,7 +2017,11 @@ public class DisplayPolicy { of.set(displayFrames.mRestricted); df.set(displayFrames.mRestricted); pf.set(displayFrames.mRestricted); - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + + // IME Insets are handled on the client for ADJUST_RESIZE in the new insets + // world + if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE + || adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.set(displayFrames.mDock); } else { cf.set(displayFrames.mContent); @@ -2718,6 +2748,18 @@ public class DisplayPolicy { } } + /** + * @see IWindowManager#setForwardedInsets + */ + public void setForwardedInsets(@NonNull Insets forwardedInsets) { + mForwardedInsets = forwardedInsets; + } + + @NonNull + public Insets getForwardedInsets() { + return mForwardedInsets; + } + @NavigationBarPosition int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { if (navigationBarCanMove() && displayWidth > displayHeight) { diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index bc165dceb544..5f341ee8002c 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -329,6 +329,15 @@ public class DisplayRotation { return mFixedToUserRotation; } + /** + * Returns {@code true} if this display rotation takes app requested orientation into + * consideration; {@code false} otherwise. For the time being the only case where this is {@code + * false} is when {@link #isFixedToUserRotation()} is {@code true}. + */ + boolean respectAppRequestedOrientation() { + return !mFixedToUserRotation; + } + public int getLandscapeRotation() { return mLandscapeRotation; } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index afae9c4ac228..a1b52f424fee 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -112,6 +112,7 @@ class InsetsStateController { * Called when a layout pass has occurred. */ void onPostLayout() { + mState.setDisplayFrame(mDisplayContent.getBounds()); for (int i = mControllers.size() - 1; i>= 0; i--) { mControllers.valueAt(i).onPostLayout(); } diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 434084cc1f3d..987492024546 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -107,10 +107,10 @@ public class Letterbox { mOuter.setEmpty(); mInner.setEmpty(); - mTop.destroy(); - mLeft.destroy(); - mBottom.destroy(); - mRight.destroy(); + mTop.remove(); + mLeft.remove(); + mBottom.remove(); + mRight.remove(); } /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */ @@ -154,9 +154,9 @@ public class Letterbox { mSurface.setColor(new float[]{0, 0, 0}); } - public void destroy() { + public void remove() { if (mSurface != null) { - mSurface.destroy(); + mSurface.remove(); mSurface = null; } } diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 3947bd47b588..84cd8d1632cf 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -626,7 +626,7 @@ class ScreenRotationAnimation { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM, " FREEZE " + mSurfaceControl + ": DESTROY"); - mSurfaceControl.destroy(); + mSurfaceControl.remove(); mSurfaceControl = null; } if (mCustomBlackFrame != null) { diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index c600e0f4a657..5ea24518370b 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -280,7 +280,7 @@ class SurfaceAnimator { } mService.mAnimationTransferMap.remove(mAnimation); if (mLeash != null && destroyLeash) { - t.reparent(mLeash, null); + t.remove(mLeash); scheduleAnim = true; } mLeash = null; diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING index bbe542458b87..b2e8bbe3b885 100644 --- a/services/core/java/com/android/server/wm/TEST_MAPPING +++ b/services/core/java/com/android/server/wm/TEST_MAPPING @@ -1,34 +1,4 @@ { - "presubmit": [ - { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.wm." - }, - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - }, - { - "name": "WmTests", - "options": [ - { - "include-filter": "com.android.server.wm." - }, - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - } - ], "postsubmit": [ { "name": "CtsWindowManagerDeviceTestCases" diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index af38c0680c51..69f0012b69ba 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -2197,11 +2197,15 @@ class TaskRecord extends ConfigurationContainer { // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent" outOverrideBounds.setEmpty(); + final boolean parentHandlesOrientationChange = mTask != null + && mTask.getParent() != null + && mTask.getParent().handlesOrientationChangeFromDescendant(); // If the task or its top activity requires a different orientation, make it fit the // available bounds by scaling down its bounds. int forcedOrientation = getTopActivityRequestedOrientation(); if (forcedOrientation != ORIENTATION_UNDEFINED - && forcedOrientation != newParentConfig.orientation) { + && forcedOrientation != newParentConfig.orientation + && !parentHandlesOrientationChange) { final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final int parentWidth = parentBounds.width(); final int parentHeight = parentBounds.height(); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 53cd5ea8bf16..7b742fd2e0f3 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1006,7 +1006,7 @@ public class TaskStack extends WindowContainer<Task> implements EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); if (mAnimationBackgroundSurface != null) { - mAnimationBackgroundSurface.destroy(); + mAnimationBackgroundSurface.remove(); mAnimationBackgroundSurface = null; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b246da444a04..76f080b233a8 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -320,7 +320,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } if (mSurfaceControl != null) { - mPendingTransaction.reparent(mSurfaceControl, null); + mPendingTransaction.remove(mSurfaceControl); // Merge to parent transaction to ensure the transactions on this WindowContainer are // applied in native even if WindowContainer is removed. @@ -715,6 +715,21 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** + * Check if this container or its parent will handle orientation changes from descendants. It's + * different from the return value of {@link #onDescendantOrientationChanged(IBinder, + * ConfigurationContainer)} in the sense that the return value of this method tells if this + * container or its parent will handle the request eventually, while the return value of the + * other method is if it handled the request synchronously. + * + * @return {@code true} if it handles or will handle orientation change in the future; {@code + * false} if it won't handle the change at anytime. + */ + boolean handlesOrientationChangeFromDescendant() { + final WindowContainer parent = getParent(); + return parent != null && parent.handlesOrientationChangeFromDescendant(); + } + + /** * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2 * parameters. * diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3bb660825843..6c3e1f4ce1c2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -134,6 +134,7 @@ import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; @@ -6438,6 +6439,23 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void setForwardedInsets(int displayId, Insets insets) throws RemoteException { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return; + } + final int callingUid = Binder.getCallingUid(); + final int displayOwnerUid = dc.getDisplay().getOwnerUid(); + if (callingUid != displayOwnerUid) { + throw new SecurityException( + "Only owner of the display can set ForwardedInsets to it."); + } + dc.setForwardedInsets(insets); + } + } + void intersectDisplayInsetBounds(Rect display, Rect insets, Rect inOutBounds) { mTmpRect3.set(display); mTmpRect3.inset(insets); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 0fb900a6eaf4..465f4131ee8e 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -986,7 +986,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override public String toString() { - return mOwner.toString(); + return mOwner != null ? mOwner.toString() : null; } public void dump(PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4bc140095f1b..85b251a99f83 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -156,6 +156,7 @@ import android.os.Binder; import android.os.Debug; import android.os.IBinder; import android.os.PowerManager; +import android.os.PowerManager.WakeReason; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -611,7 +612,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } interface PowerManagerWrapper { - void wakeUp(long time, String reason); + void wakeUp(long time, @WakeReason int reason, String details); boolean isInteractive(); @@ -623,8 +624,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP this(service, s, c, token, parentWindow, appOp, seq, a, viewVisibility, ownerId, ownerCanAddInternalSystemWindow, new PowerManagerWrapper() { @Override - public void wakeUp(long time, String reason) { - service.mPowerManager.wakeUp(time, reason); + public void wakeUp(long time, @WakeReason int reason, String details) { + service.mPowerManager.wakeUp(time, reason, details); } @Override @@ -2253,7 +2254,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Slog.v(TAG, "Relayout window turning screen on: " + this); } mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(), - "android.server.wm:TURN_ON"); + PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG"); } if (mAppToken != null) { diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index dea3597989be..e796b99f3989 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -161,7 +161,7 @@ class WindowSurfaceController { } try { if (mSurfaceControl != null) { - mSurfaceControl.destroy(); + mSurfaceControl.remove(); } } catch (RuntimeException e) { Slog.w(TAG, "Error destroying surface in: " + this, e); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 33317b554988..c18e98bab9c4 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -373,6 +373,9 @@ void NativeInputManager::dump(std::string& dump) { mInputManager->getReader()->dump(dump); dump += "\n"; + mInputManager->getClassifier()->dump(dump); + dump += "\n"; + mInputManager->getDispatcher()->dump(dump); dump += "\n"; } diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index bf96f9a3b71b..6cd9f2c718ee 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -122,7 +122,7 @@ class JavaByteArrayHolder { } ~JavaByteArrayHolder() { - LOG_ALWAYS_FATAL_IF(mElements == nullptr, "Elements are not released"); + LOG_ALWAYS_FATAL_IF(mElements != nullptr, "Elements are not released"); } private: diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1155f499d619..e30acf71d328 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12790,10 +12790,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - // TODO(b/22388012): When backup is available for secondary users and profiles, consider - // whether there are any privacy/security implications of enabling the backup service here - // if there are other users or profiles unmanaged or managed by a different entity (i.e. not - // affiliated). @Override public void setBackupServiceEnabled(ComponentName admin, boolean enabled) { if (!mHasFeature) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ab30cda271f0..a6017f2c1e86 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -64,6 +64,7 @@ import android.util.EventLog; import android.util.Slog; import android.util.TimingsTraceLog; import android.view.WindowManager; +import android.view.contentcapture.ContentCaptureManager; import android.view.inputmethod.InputMethodSystemProperty; import com.android.internal.R; @@ -2214,33 +2215,30 @@ public final class SystemServer { } private void startContentCaptureService(@NonNull Context context) { - // Check if it was explicitly enabled by DeviceConfig - final String settings = DeviceConfig.getProperty(DeviceConfig.ContentCapture.NAMESPACE, - DeviceConfig.ContentCapture.PROPERTY_CONTENTCAPTURE_ENABLED); - if (settings == null) { - // Better be safe than sorry... - Slog.d(TAG, "ContentCaptureService disabled because its not set by OEM"); - return; - } - switch (settings) { - case "always": - // Should be used only during development + // First check if it was explicitly enabled by DeviceConfig + boolean explicitlySupported = false; + String settings = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); + if (settings != null && !settings.equalsIgnoreCase("default")) { + explicitlySupported = Boolean.parseBoolean(settings); + if (explicitlySupported) { Slog.d(TAG, "ContentCaptureService explicitly enabled by DeviceConfig"); - break; - case "default": - // Default case: check if OEM overlaid the resource that defines the service. - final String serviceName = context.getString( - com.android.internal.R.string.config_defaultContentCaptureService); - if (TextUtils.isEmpty(serviceName)) { - Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); - return; - } - break; - default: - // Kill switch for OEMs - Slog.d(TAG, "ContentCaptureService disabled because its set to: " + settings); + } else { + Slog.d(TAG, "ContentCaptureService explicitly disabled by DeviceConfig"); return; + } } + + // Then check if OEM overlaid the resource that defines the service. + if (!explicitlySupported) { + final String serviceName = context + .getString(com.android.internal.R.string.config_defaultContentCaptureService); + if (TextUtils.isEmpty(serviceName)) { + Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); + return; + } + } + traceBeginAndSlog("StartContentCaptureService"); mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS); traceEnd(); diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java index b8db3f39eb62..37909c3022d4 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java @@ -62,6 +62,7 @@ import org.robolectric.shadows.ShadowContextWrapper; import java.io.File; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; /** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ @@ -1516,8 +1517,7 @@ public class BackupManagerServiceTest { public void testDump_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); - File testFile = new File(mContext.getFilesDir(), "test"); - testFile.createNewFile(); + File testFile = createTestFile(); FileDescriptor fileDescriptor = new FileDescriptor(); PrintWriter printWriter = new PrintWriter(testFile); String[] args = {"1", "2"}; @@ -1531,8 +1531,7 @@ public class BackupManagerServiceTest { @Test public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception { BackupManagerService backupManagerService = createService(); - File testFile = new File(mContext.getFilesDir(), "test"); - testFile.createNewFile(); + File testFile = createTestFile(); FileDescriptor fileDescriptor = new FileDescriptor(); PrintWriter printWriter = new PrintWriter(testFile); String[] args = {"1", "2"}; @@ -1542,6 +1541,12 @@ public class BackupManagerServiceTest { verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args); } + private File createTestFile() throws IOException { + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + return testFile; + } + private BackupManagerService createService() { mShadowContext.grantPermissions(BACKUP); return new BackupManagerService( diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index 427aed7364d2..c8d1eb413ecf 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -76,6 +76,7 @@ import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import java.io.File; +import java.io.IOException; import java.util.List; /** @@ -1158,6 +1159,58 @@ public class UserBackupManagerServiceTest { } /** + * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns {@code -1} + * when value not set. + */ + @Test + public void testGetAncestralSerialNumber_notSet_returnsMinusOne() { + UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks(); + + assertThat(service.getAncestralSerialNumber()).isEqualTo(-1L); + } + + /** + * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value + * when value set. + */ + @Test + public void testGetAncestralSerialNumber_set_returnsCorrectValue() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks(); + service.setAncestralSerialNumberFile(createTestFile()); + + long testSerialNumber = 20L; + service.setAncestralSerialNumber(testSerialNumber); + + assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber); + } + + /** + * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value + * when value set. + */ + @Test + public void testGetAncestralSerialNumber_setTwice_returnsCorrectValue() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks(); + service.setAncestralSerialNumberFile(createTestFile()); + + long testSerialNumber = 20L; + long testSerialNumber2 = 21L; + service.setAncestralSerialNumber(testSerialNumber); + service.setAncestralSerialNumber(testSerialNumber2); + + assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2); + } + + private File createTestFile() throws IOException { + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + return testFile; + } + + + /** * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method. */ diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java new file mode 100644 index 000000000000..8df08262c9fa --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java @@ -0,0 +1,57 @@ +/* + * 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.server.backup.encryption.chunking; + +import static org.junit.Assert.assertEquals; +import static org.testng.Assert.assertThrows; + +import android.platform.test.annotations.Presubmit; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link ByteRange}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class ByteRangeTest { + @Test + public void getLength_includesEnd() throws Exception { + ByteRange byteRange = new ByteRange(5, 10); + + int length = byteRange.getLength(); + + assertEquals(6, length); + } + + @Test + public void constructor_rejectsNegativeStart() { + assertThrows(IllegalArgumentException.class, () -> new ByteRange(-1, 10)); + } + + @Test + public void constructor_rejectsEndBeforeStart() { + assertThrows(IllegalArgumentException.class, () -> new ByteRange(10, 9)); + } + + @Test + public void extend_withZeroLength_throwsException() { + ByteRange byteRange = new ByteRange(5, 10); + + assertThrows(IllegalArgumentException.class, () -> byteRange.extend(0)); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java new file mode 100644 index 000000000000..2af6f2bee8ff --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java @@ -0,0 +1,90 @@ +/* + * 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.server.backup.encryption.chunking; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.platform.test.annotations.Presubmit; + +import com.google.common.primitives.Bytes; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; + +import java.io.IOException; + +/** Tests for {@link DiffScriptBackupWriter}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class DiffScriptBackupWriterTest { + private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + @Captor private ArgumentCaptor<Byte> mBytesCaptor; + @Mock private SingleStreamDiffScriptWriter mDiffScriptWriter; + private BackupWriter mBackupWriter; + + @Before + public void setUp() { + mDiffScriptWriter = mock(SingleStreamDiffScriptWriter.class); + mBackupWriter = new DiffScriptBackupWriter(mDiffScriptWriter); + mBytesCaptor = ArgumentCaptor.forClass(Byte.class); + } + + @Test + public void writeBytes_writesBytesToWriter() throws Exception { + mBackupWriter.writeBytes(TEST_BYTES); + + verify(mDiffScriptWriter, atLeastOnce()).writeByte(mBytesCaptor.capture()); + assertThat(mBytesCaptor.getAllValues()) + .containsExactlyElementsIn(Bytes.asList(TEST_BYTES)) + .inOrder(); + } + + @Test + public void writeChunk_writesChunkToWriter() throws Exception { + mBackupWriter.writeChunk(0, 10); + + verify(mDiffScriptWriter).writeChunk(0, 10); + } + + @Test + public void getBytesWritten_returnsTotalSum() throws Exception { + mBackupWriter.writeBytes(TEST_BYTES); + mBackupWriter.writeBytes(TEST_BYTES); + mBackupWriter.writeChunk(/*start=*/ 0, /*length=*/ 10); + + long bytesWritten = mBackupWriter.getBytesWritten(); + + assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length + 10); + } + + @Test + public void flush_flushesWriter() throws IOException { + mBackupWriter.flush(); + + verify(mDiffScriptWriter).flush(); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java new file mode 100644 index 000000000000..73baf80a2c70 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java @@ -0,0 +1,128 @@ +/* + * 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.server.backup.encryption.chunking; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.assertThrows; + +import android.platform.test.annotations.Presubmit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Locale; + +/** Tests for {@link SingleStreamDiffScriptWriter}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class SingleStreamDiffScriptWriterTest { + private static final int MAX_CHUNK_SIZE_IN_BYTES = 256; + /** By default this Locale does not use Arabic numbers for %d formatting. */ + private static final Locale HINDI = new Locale("hi", "IN"); + + private Locale mDefaultLocale; + private ByteArrayOutputStream mOutputStream; + private SingleStreamDiffScriptWriter mDiffScriptWriter; + + @Before + public void setUp() { + mDefaultLocale = Locale.getDefault(); + mOutputStream = new ByteArrayOutputStream(); + mDiffScriptWriter = + new SingleStreamDiffScriptWriter(mOutputStream, MAX_CHUNK_SIZE_IN_BYTES); + } + + @After + public void tearDown() { + Locale.setDefault(mDefaultLocale); + } + + @Test + public void writeChunk_withNegativeStart_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> mDiffScriptWriter.writeChunk(-1, 50)); + } + + @Test + public void writeChunk_withZeroLength_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> mDiffScriptWriter.writeChunk(0, 0)); + } + + @Test + public void writeChunk_withExistingBytesInBuffer_writesBufferFirst() + throws IOException { + String testString = "abcd"; + writeStringAsBytesToWriter(testString, mDiffScriptWriter); + + mDiffScriptWriter.writeChunk(0, 20); + mDiffScriptWriter.flush(); + + // Expected format: length of abcd, newline, abcd, newline, chunk start - chunk end + assertThat(mOutputStream.toString("UTF-8")).isEqualTo( + String.format("%d\n%s\n%d-%d\n", testString.length(), testString, 0, 19)); + } + + @Test + public void writeChunk_overlappingPreviousChunk_combinesChunks() throws IOException { + mDiffScriptWriter.writeChunk(3, 4); + + mDiffScriptWriter.writeChunk(7, 5); + mDiffScriptWriter.flush(); + + assertThat(mOutputStream.toString("UTF-8")).isEqualTo(String.format("3-11\n")); + } + + @Test + public void writeChunk_formatsByteIndexesUsingArabicNumbers() throws Exception { + Locale.setDefault(HINDI); + + mDiffScriptWriter.writeChunk(0, 12345); + mDiffScriptWriter.flush(); + + assertThat(mOutputStream.toString("UTF-8")).isEqualTo("0-12344\n"); + } + + @Test + public void flush_flushesOutputStream() throws IOException { + ByteArrayOutputStream mockOutputStream = mock(ByteArrayOutputStream.class); + SingleStreamDiffScriptWriter diffScriptWriter = + new SingleStreamDiffScriptWriter(mockOutputStream, MAX_CHUNK_SIZE_IN_BYTES); + + diffScriptWriter.flush(); + + verify(mockOutputStream).flush(); + } + + private void writeStringAsBytesToWriter(String string, SingleStreamDiffScriptWriter writer) + throws IOException { + byte[] bytes = string.getBytes("UTF-8"); + for (int i = 0; i < bytes.length; i++) { + writer.writeByte(bytes[i]); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java index 2f8e5456a695..63015be6ef3f 100644 --- a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java @@ -147,10 +147,10 @@ public final class AppCompactorTest { KEY_USE_COMPACTION, "true", false); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_1, - Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 3) + 1), false); + Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_2, - Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 3) + 1), false); + Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_THROTTLE_1, Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); @@ -173,9 +173,9 @@ public final class AppCompactorTest { assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true)); assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 3) + 1))); + is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1))); assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 3) + 1))); + is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1))); assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1)); assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, @@ -233,13 +233,13 @@ public final class AppCompactorTest { // When we override new values for the compaction action with reasonable values... - // There are three possible values for compactAction[Some|Full]. - for (int i = 1; i < 4; i++) { + // There are four possible values for compactAction[Some|Full]. + for (int i = 1; i < 5; i++) { mCountDown = new CountDownLatch(2); - int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 3 + 1; + int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false); - int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 3 + 1; + int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false); assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index e3b1245f20a5..7081d2e3b370 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -962,13 +962,13 @@ public class BrightnessTrackerTest { } @Override - public int getColorTemperature(Context context, int userId) { + public int getNightDisplayColorTemperature(Context context) { return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, mDefaultNightModeColorTemperature); } @Override - public boolean isNightModeActive(Context context, int userId) { + public boolean isNightDisplayActivated(Context context) { return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0) == 1; } diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java index 0b01657868a8..5900fc57296c 100644 --- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java @@ -26,6 +26,7 @@ import android.app.AlarmManager; import android.content.Context; import android.content.ContextWrapper; import android.hardware.display.ColorDisplayManager; +import android.hardware.display.Time; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; @@ -37,7 +38,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; -import com.android.internal.app.ColorDisplayController; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -71,7 +71,6 @@ public class ColorDisplayServiceTest { private MockTwilightManager mTwilightManager; private ColorDisplayService mColorDisplayService; - private ColorDisplayController mColorDisplayController; private ColorDisplayService.BinderService mBinderService; @BeforeClass @@ -97,7 +96,6 @@ public class ColorDisplayServiceTest { mTwilightManager = new MockTwilightManager(); LocalServices.addService(TwilightManager.class, mTwilightManager); - mColorDisplayController = new ColorDisplayController(mContext, mUserId); mColorDisplayService = new ColorDisplayService(mContext); mBinderService = mColorDisplayService.new BinderService(); } @@ -988,9 +986,11 @@ public class ColorDisplayServiceTest { * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes) */ private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) { - mColorDisplayController.setAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); - mColorDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset)); - mColorDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset)); + mBinderService.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); + mBinderService.setNightDisplayCustomStartTime( + new Time(getLocalTimeRelativeToNow(startTimeOffset))); + mBinderService + .setNightDisplayCustomEndTime(new Time(getLocalTimeRelativeToNow(endTimeOffset))); } /** @@ -1000,7 +1000,7 @@ public class ColorDisplayServiceTest { * @param sunriseOffset the offset relative to now for sunrise (in minutes) */ private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) { - mColorDisplayController.setAutoMode(ColorDisplayManager.AUTO_MODE_TWILIGHT); + mBinderService.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_TWILIGHT); mTwilightManager.setTwilightState( getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset)); } @@ -1041,22 +1041,18 @@ public class ColorDisplayServiceTest { } /** - * Configures color mode via ColorDisplayController. - * - * @param colorMode the color mode to set + * Configures color mode. */ private void setColorMode(int colorMode) { - mColorDisplayController.setColorMode(colorMode); + mBinderService.setColorMode(colorMode); } /** * Returns whether the color mode is valid on the device the tests are running on. - * - * @param mode the mode to check */ private boolean isColorModeValid(int mode) { final int[] availableColorModes = mContext.getResources().getIntArray( - R.array.config_availableColorModes); + R.array.config_availableColorModes); if (availableColorModes != null) { for (int availableMode : availableColorModes) { if (mode == availableMode) { @@ -1073,12 +1069,9 @@ public class ColorDisplayServiceTest { private void startService() { Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId); - InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); - mColorDisplayService.onStartUser(mUserId); - } + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + mColorDisplayService.onStartUser(mUserId); }); } @@ -1100,7 +1093,7 @@ public class ColorDisplayServiceTest { */ private void assertActiveColorMode(int mode) { assertWithMessage("Unexpected color mode setting") - .that(mColorDisplayController.getColorMode()) + .that(mBinderService.getColorMode()) .isEqualTo(mode); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 85909d564a7b..72357ceee099 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -31,7 +31,6 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; -import android.content.pm.UsesPermissionInfo; import android.os.Bundle; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -466,7 +465,6 @@ public class PackageParserTest { pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo())); pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo())); pkg.requestedPermissions.add("foo7"); - pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7")); pkg.implicitPermissions.add("foo25"); pkg.protectedBroadcasts = new ArrayList<>(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 2ddc71f570b8..48ab8d6698bd 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -141,7 +141,8 @@ public class DexManagerTests { assertIsUsedByOtherApps(mBarUser0, pui, true); assertTrue(pui.getDexUseInfoMap().isEmpty()); - assertHasDclInfo(mBarUser0, mFooUser0, mBarUser0.getBaseAndSplitDexPaths()); + // A package loading another package's APK is not DCL (it's not app data). + assertNoDclInfo(mBarUser0); } @Test @@ -334,7 +335,9 @@ public class DexManagerTests { notifyDexLoad(mFooUser0, newSplits, mUser0); PackageUseInfo pui = getPackageUseInfo(mBarUser0); assertIsUsedByOtherApps(newSplits, pui, true); - assertHasDclInfo(mBarUser0, mFooUser0, newSplits); + + // Primary and split APKs are not recorded as DCL. + assertNoDclInfo(mBarUser0); } @Test diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index bfa0b74b6927..63341b6ea38a 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -150,7 +150,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { @SmallTest public void testGetDesiredScreenPolicy_WithVR() throws Exception { // Brighten up the screen - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, 0); + mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); @@ -160,12 +160,13 @@ public class PowerManagerServiceTest extends AndroidTestCase { DisplayPowerRequest.POLICY_VR); // Then take a nap - mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, 0); + mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + 0); assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( DisplayPowerRequest.POLICY_OFF); // Wake up to VR - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, 0); + mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( DisplayPowerRequest.POLICY_VR); diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java index 43e3eb0193b1..50dbaf570e9e 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java @@ -18,15 +18,19 @@ package com.android.server.rollback; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.util.IntArray; +import android.util.SparseLongArray; import com.android.server.pm.Installer; @@ -38,6 +42,7 @@ import org.mockito.Mockito; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; @RunWith(JUnit4.class) public class AppDataRollbackHelperTest { @@ -50,10 +55,14 @@ public class AppDataRollbackHelperTest { // All users are unlocked so we should snapshot data for them. doReturn(true).when(helper).isUserCredentialLocked(eq(10)); doReturn(true).when(helper).isUserCredentialLocked(eq(11)); - IntArray pending = helper.snapshotAppData("com.foo.bar", new int[]{10, 11}); - assertEquals(2, pending.size()); - assertEquals(10, pending.get(0)); - assertEquals(11, pending.get(1)); + AppDataRollbackHelper.SnapshotAppDataResult result = helper.snapshotAppData("com.foo.bar", + new int[]{10, 11}); + + assertEquals(2, result.pendingBackups.size()); + assertEquals(10, result.pendingBackups.get(0)); + assertEquals(11, result.pendingBackups.get(1)); + + assertEquals(0, result.ceSnapshotInodes.size()); InOrder inOrder = Mockito.inOrder(installer); inOrder.verify(installer).snapshotAppData( @@ -65,10 +74,14 @@ public class AppDataRollbackHelperTest { // One of the users is unlocked but the other isn't doReturn(false).when(helper).isUserCredentialLocked(eq(10)); doReturn(true).when(helper).isUserCredentialLocked(eq(11)); + when(installer.snapshotAppData(anyString(), anyInt(), anyInt())).thenReturn(239L); - pending = helper.snapshotAppData("com.foo.bar", new int[]{10, 11}); - assertEquals(1, pending.size()); - assertEquals(11, pending.get(0)); + result = helper.snapshotAppData("com.foo.bar", new int[]{10, 11}); + assertEquals(1, result.pendingBackups.size()); + assertEquals(11, result.pendingBackups.get(0)); + + assertEquals(1, result.ceSnapshotInodes.size()); + assertEquals(239L, result.ceSnapshotInodes.get(10)); inOrder = Mockito.inOrder(installer); inOrder.verify(installer).snapshotAppData( @@ -83,7 +96,7 @@ public class AppDataRollbackHelperTest { RollbackData data = new RollbackData(1, new File("/does/not/exist"), -1, true); data.packages.add(new PackageRollbackInfo( new VersionedPackage(packageName, 1), new VersionedPackage(packageName, 1), - new IntArray(), new ArrayList<>(), false)); + new IntArray(), new ArrayList<>(), false, new IntArray(), new SparseLongArray())); data.inProgress = true; return data; @@ -173,4 +186,53 @@ public class AppDataRollbackHelperTest { ArrayList<RestoreInfo> pendingRestores = rd.packages.get(0).getPendingRestores(); assertEquals(0, pendingRestores.size()); } + + @Test + public void destroyAppData() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); + SparseLongArray ceSnapshotInodes = new SparseLongArray(); + ceSnapshotInodes.put(11, 239L); + + helper.destroyAppDataSnapshot("com.foo.bar", 10, 0L); + helper.destroyAppDataSnapshot("com.foo.bar", 11, 239L); + + InOrder inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).destroyAppDataSnapshot( + eq("com.foo.bar"), eq(10), eq(0L), + eq(Installer.FLAG_STORAGE_DE)); + inOrder.verify(installer).destroyAppDataSnapshot( + eq("com.foo.bar"), eq(11), eq(239L), + eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void commitPendingBackupAndRestoreForUser_updatesRollbackData() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); + + ArrayList<RollbackData> changedRollbackData = new ArrayList<>(); + changedRollbackData.add(createInProgressRollbackData("com.foo.bar")); + + when(installer.snapshotAppData(anyString(), anyInt(), anyInt())).thenReturn(239L); + + ArrayList<String> pendingBackups = new ArrayList<>(); + pendingBackups.add("com.foo.bar"); + + helper.commitPendingBackupAndRestoreForUser(11, pendingBackups, + new HashMap<>() /* pendingRestores */, changedRollbackData); + + assertEquals(1, changedRollbackData.size()); + assertEquals(1, changedRollbackData.get(0).packages.size()); + PackageRollbackInfo info = changedRollbackData.get(0).packages.get(0); + + assertEquals(1, info.getCeSnapshotInodes().size()); + assertEquals(239L, info.getCeSnapshotInodes().get(11)); + + InOrder inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).snapshotAppData("com.foo.bar", 11 /* userId */, + Installer.FLAG_STORAGE_CE); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java new file mode 100644 index 000000000000..3e01fb58c754 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.notification; + +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.Notification.Builder; +import android.app.NotificationChannel; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BubbleExtractorTest extends UiServiceTestCase { + + @Mock RankingConfig mConfig; + + private String mPkg = "com.android.server.notification"; + private int mId = 1001; + private String mTag = null; + private int mUid = 1000; + private int mPid = 2000; + private UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + private NotificationRecord getNotificationRecord(boolean allow, int importanceHigh) { + NotificationChannel channel = new NotificationChannel("a", "a", importanceHigh); + channel.setAllowBubbles(allow); + when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel); + + final Builder builder = new Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setPriority(Notification.PRIORITY_HIGH) + .setDefaults(Notification.DEFAULT_SOUND); + + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, + mPid, n, mUser, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + return r; + } + + // + // Tests + // + + @Test + public void testAppYesChannelNo() { + BubbleExtractor extractor = new BubbleExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.bubblesEnabled(mUser)).thenReturn(true); + when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true); + NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED); + + extractor.process(r); + + assertFalse(r.canBubble()); + } + + @Test + public void testAppNoChannelYes() throws Exception { + BubbleExtractor extractor = new BubbleExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.bubblesEnabled(mUser)).thenReturn(true); + when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false); + NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH); + + extractor.process(r); + + assertFalse(r.canBubble()); + } + + @Test + public void testAppYesChannelYes() { + BubbleExtractor extractor = new BubbleExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.bubblesEnabled(mUser)).thenReturn(true); + when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true); + NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED); + + extractor.process(r); + + assertTrue(r.canBubble()); + } + + @Test + public void testAppNoChannelNo() { + BubbleExtractor extractor = new BubbleExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.bubblesEnabled(mUser)).thenReturn(true); + when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false); + NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED); + + extractor.process(r); + + assertFalse(r.canBubble()); + } + + @Test + public void testAppYesChannelYesUserNo() { + BubbleExtractor extractor = new BubbleExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.bubblesEnabled(mUser)).thenReturn(false); + when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true); + NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH); + + extractor.process(r); + + assertFalse(r.canBubble()); + } +} 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 20f72bfe9938..7a530dfe306f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -74,6 +74,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -305,6 +306,82 @@ public class ManagedServicesTest extends UiServiceTestCase { } } + /** Test that restore ignores the user id attribute and applies the data to the target user. */ + @Test + public void testReadXml_onlyRestoresForTargetUser() throws Exception { + for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { + ManagedServices service = + new TestManagedServices( + getContext(), mLock, mUserProfiles, mIpm, approvalLevel); + String testPackage = "user.test.package"; + String testComponent = "user.test.component/C1"; + String resolvedValue = + (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; + XmlPullParser parser = + getParserWithEntries(service, getXmlEntry(resolvedValue, 0, true)); + + service.readXml(parser, null, true, 10); + + assertFalse(service.isPackageOrComponentAllowed(resolvedValue, 0)); + assertTrue(service.isPackageOrComponentAllowed(resolvedValue, 10)); + } + } + + /** Test that backup only writes packages/components that belong to the target user. */ + @Test + public void testWriteXml_onlyBackupsForTargetUser() throws Exception { + for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { + ManagedServices service = + new TestManagedServices( + getContext(), mLock, mUserProfiles, mIpm, approvalLevel); + // Set up components. + String testPackage0 = "user0.test.package"; + String testComponent0 = "user0.test.component/C1"; + String testPackage10 = "user10.test.package"; + String testComponent10 = "user10.test.component/C1"; + String resolvedValue0 = + (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent0 : testPackage0; + String resolvedValue10 = + (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent10 : testPackage10; + addExpectedServices( + service, Collections.singletonList(service.getPackageName(resolvedValue0)), 0); + addExpectedServices( + service, + Collections.singletonList(service.getPackageName(resolvedValue10)), + 10); + XmlPullParser parser = + getParserWithEntries( + service, + getXmlEntry(resolvedValue0, 0, true), + getXmlEntry(resolvedValue10, 10, true)); + service.readXml(parser, null, false, UserHandle.USER_ALL); + + // Write backup. + XmlSerializer serializer = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + service.writeXml(serializer, true, 10); + serializer.endDocument(); + serializer.flush(); + + // Reset values. + service.setPackageOrComponentEnabled(resolvedValue0, 0, true, false); + service.setPackageOrComponentEnabled(resolvedValue10, 10, true, false); + + // Parse backup via restore. + XmlPullParser restoreParser = Xml.newPullParser(); + restoreParser.setInput( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); + restoreParser.nextTag(); + service.readXml(restoreParser, null, true, 10); + + assertFalse(service.isPackageOrComponentAllowed(resolvedValue0, 0)); + assertFalse(service.isPackageOrComponentAllowed(resolvedValue0, 10)); + assertTrue(service.isPackageOrComponentAllowed(resolvedValue10, 10)); + } + } + @Test public void testWriteXml_trimsMissingServices() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { @@ -348,7 +425,9 @@ public class ManagedServicesTest extends UiServiceTestCase { ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); - service.writeXml(serializer, true); + for (UserInfo userInfo : mUm.getUsers()) { + service.writeXml(serializer, true, userInfo.id); + } serializer.endDocument(); serializer.flush(); @@ -356,7 +435,9 @@ public class ManagedServicesTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - service.readXml(parser, null); + for (UserInfo userInfo : mUm.getUsers()) { + service.readXml(parser, null, true, userInfo.id); + } verifyExpectedApprovedEntries(service); assertFalse(service.isPackageOrComponentAllowed("this.is.a.package.name", 0)); @@ -376,7 +457,7 @@ public class ManagedServicesTest extends UiServiceTestCase { ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); - service.writeXml(serializer, false); + service.writeXml(serializer, false, UserHandle.USER_ALL); serializer.endDocument(); serializer.flush(); @@ -921,7 +1002,23 @@ public class ManagedServicesTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.toString().getBytes())), null); parser.nextTag(); - service.readXml(parser, null); + service.readXml(parser, null, false, UserHandle.USER_ALL); + } + + private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) + throws Exception { + final StringBuffer xml = new StringBuffer(); + xml.append("<" + service.getConfig().xmlTag + ">\n"); + for (String xmlEntry : xmlEntries) { + xml.append(xmlEntry); + } + xml.append("</" + service.getConfig().xmlTag + ">"); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml.toString().getBytes())), null); + parser.nextTag(); + return parser; } private void addExpectedServices(final ManagedServices service, final List<String> packages, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 0b488c09b45a..19b567f6951b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -33,6 +33,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; +import android.os.UserHandle; import android.os.UserManager; import android.util.IntArray; import android.util.Xml; @@ -129,7 +130,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.toString().getBytes())), null); parser.nextTag(); - mAssistants.readXml(parser, null); + mAssistants.readXml(parser, null, false, UserHandle.USER_ALL); verify(mNm, never()).readDefaultAssistant(anyInt()); verify(mAssistants, times(1)).addApprovedList( diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index daca9cb28051..2162f282e134 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -54,7 +54,7 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class NotificationListenerServiceTest extends UiServiceTestCase { - private String[] mKeys = new String[] { "key", "key1", "key2", "key3"}; + private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"}; @Test public void testGetActiveNotifications_notNull() throws Exception { @@ -70,7 +70,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } @Test - public void testRanking() throws Exception { + public void testRanking() { TestListenerService service = new TestListenerService(); service.applyUpdateLocked(generateUpdate()); for (int i = 0; i < mKeys.length; i++) { @@ -92,6 +92,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(lastAudiblyAlerted(i), ranking.getLastAudiblyAlertedMillis()); assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions()); assertEquals(getSmartReplies(key, i), ranking.getSmartReplies()); + assertEquals(canBubble(i), ranking.canBubble()); } } @@ -112,6 +113,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { Bundle smartReplies = new Bundle(); Bundle lastAudiblyAlerted = new Bundle(); Bundle noisy = new Bundle(); + boolean[] canBubble = new boolean[mKeys.length]; for (int i = 0; i < mKeys.length; i++) { String key = mKeys[i]; @@ -133,12 +135,13 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i)); lastAudiblyAlerted.putLong(key, lastAudiblyAlerted(i)); noisy.putBoolean(key, getNoisy(i)); + canBubble[i] = canBubble(i); } NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys, interceptedKeys.toArray(new String[0]), visibilityOverrides, suppressedVisualEffects, importance, explanation, overrideGroupKeys, channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden, - smartActions, smartReplies, lastAudiblyAlerted, noisy); + smartActions, smartReplies, lastAudiblyAlerted, noisy, canBubble); return update; } @@ -235,6 +238,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return choices; } + private boolean canBubble(int index) { + return index % 4 == 0; + } + private void assertActionsEqual( List<Notification.Action> expecteds, List<Notification.Action> actuals) { assertEquals(expecteds.size(), actuals.size()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 72aa0f2ba09e..cc621387691a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -123,6 +123,7 @@ import com.android.server.lights.Light; import com.android.server.lights.LightsManager; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; +import com.android.server.pm.UserManagerService; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -206,15 +207,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { UriGrantsManagerInternal mUgmInternal; @Mock AppOpsManager mAppOpsManager; + @Mock + private UserManagerService mUserMangerService; // Use a Testable subclass so we can simulate calls from the system without failing. private static class TestableNotificationManagerService extends NotificationManagerService { int countSystemChecks = 0; boolean isSystemUid = true; int countLogSmartSuggestionsVisible = 0; + UserManagerService mUserManagerService; - public TestableNotificationManagerService(Context context) { + TestableNotificationManagerService(Context context, UserManagerService userManagerService) { super(context); + mUserManagerService = userManagerService; } @Override @@ -250,7 +255,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { countLogSmartSuggestionsVisible++; } - + @Override + UserManagerService getUserManagerService() { + return mUserManagerService; + } } private class TestableToastCallback extends ITransientNotification.Stub { @@ -267,17 +275,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - // most tests assume badging is enabled - Secure.putIntForUser(getContext().getContentResolver(), - Secure.NOTIFICATION_BADGING, 1, - UserHandle.getUserHandleForUid(mUid).getIdentifier()); - LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); - mService = new TestableNotificationManagerService(mContext); + mService = new TestableNotificationManagerService(mContext, mUserMangerService); // Use this testable looper. mTestableLooper = TestableLooper.get(this); @@ -426,6 +429,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { r.setShowBadge(!r.canShowBadge()); return null; }); + answers.put("bubbles", invocationOnMock -> { + NotificationRecord r = (NotificationRecord) invocationOnMock.getArguments()[0]; + r.setAllowBubble(!r.canBubble()); + return null; + }); answers.put("package visibility", invocationOnMock -> { ((NotificationRecord) invocationOnMock.getArguments()[0]).setPackageVisibilityOverride( Notification.VISIBILITY_SECRET); @@ -1847,7 +1855,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testHasCompanionDevice_noService() throws Exception { - mService = new TestableNotificationManagerService(mContext); + mService = new TestableNotificationManagerService(mContext, mUserMangerService); assertFalse(mService.hasCompanionDevice(mListener)); } @@ -2500,10 +2508,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { + "</dnd_apps>" + "</notification-policy>"; mService.readPolicyXml( - new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false); - verify(mListeners, times(1)).readXml(any(), any()); - verify(mConditionProviders, times(1)).readXml(any(), any()); - verify(mAssistants, times(1)).readXml(any(), any()); + new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), + false, + UserHandle.USER_ALL); + verify(mListeners, times(1)).readXml(any(), any(), anyBoolean(), anyInt()); + verify(mConditionProviders, times(1)).readXml(any(), any(), anyBoolean(), anyInt()); + verify(mAssistants, times(1)).readXml(any(), any(), anyBoolean(), anyInt()); // numbers are inflated for setup verify(mListeners, times(1)).migrateToXml(); @@ -2518,10 +2528,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { + "<ranking></ranking>" + "</notification-policy>"; mService.readPolicyXml( - new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false); - verify(mListeners, never()).readXml(any(), any()); - verify(mConditionProviders, never()).readXml(any(), any()); - verify(mAssistants, never()).readXml(any(), any()); + new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), + false, + UserHandle.USER_ALL); + verify(mListeners, never()).readXml(any(), any(), anyBoolean(), anyInt()); + verify(mConditionProviders, never()).readXml(any(), any(), anyBoolean(), anyInt()); + verify(mAssistants, never()).readXml(any(), any(), anyBoolean(), anyInt()); // numbers are inflated for setup verify(mListeners, times(2)).migrateToXml(); @@ -2530,6 +2542,53 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mAssistants, times(2)).ensureAssistant(); } + @Test + public void testReadPolicyXml_doesNotRestoreManagedServicesForManagedUser() throws Exception { + final String policyXml = "<notification-policy version=\"1\">" + + "<ranking></ranking>" + + "<enabled_listeners>" + + "<service_listing approved=\"test\" user=\"10\" primary=\"true\" />" + + "</enabled_listeners>" + + "<enabled_assistants>" + + "<service_listing approved=\"test\" user=\"10\" primary=\"true\" />" + + "</enabled_assistants>" + + "<dnd_apps>" + + "<service_listing approved=\"test\" user=\"10\" primary=\"true\" />" + + "</dnd_apps>" + + "</notification-policy>"; + when(mUserMangerService.isManagedProfile(10)).thenReturn(true); + mService.readPolicyXml( + new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())), + true, + 10); + verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10)); + verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10)); + verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10)); + } + + @Test + public void testReadPolicyXml_restoresManagedServicesForNonManagedUser() throws Exception { + final String policyXml = "<notification-policy version=\"1\">" + + "<ranking></ranking>" + + "<enabled_listeners>" + + "<service_listing approved=\"test\" user=\"10\" primary=\"true\" />" + + "</enabled_listeners>" + + "<enabled_assistants>" + + "<service_listing approved=\"test\" user=\"10\" primary=\"true\" />" + + "</enabled_assistants>" + + "<dnd_apps>" + + "<service_listing approved=\"test\" user=\"10\" primary=\"true\" />" + + "</dnd_apps>" + + "</notification-policy>"; + when(mUserMangerService.isManagedProfile(10)).thenReturn(false); + mService.readPolicyXml( + new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())), + true, + 10); + verify(mListeners, times(1)).readXml(any(), any(), eq(true), eq(10)); + verify(mConditionProviders, times(1)).readXml(any(), any(), eq(true), eq(10)); + verify(mAssistants, times(1)).readXml(any(), any(), eq(true), eq(10)); + } @Test public void testLocaleChangedCallsUpdateDefaultZenModeRules() throws Exception { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 47ec390d6b01..05bb30742fbd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -149,6 +149,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { contentResolver.setFallbackToExisting(false); Secure.putIntForUser(contentResolver, Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID_N_MR1)); + Secure.putIntForUser(contentResolver, + Secure.NOTIFICATION_BUBBLES, 1, UserHandle.getUserId(UID_N_MR1)); ContentProvider testContentProvider = mock(ContentProvider.class); when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); @@ -174,14 +176,14 @@ public class PreferencesHelperTest extends UiServiceTestCase { .build(); } - private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup, - String... channelIds) + private ByteArrayOutputStream writeXmlAndPurge( + String pkg, int uid, boolean forBackup, int userId, String... channelIds) throws Exception { XmlSerializer serializer = new FastXmlSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); - mHelper.writeXml(serializer, forBackup); + mHelper.writeXml(serializer, forBackup, userId); serializer.endDocument(); serializer.flush(); for (String channelId : channelIds) { @@ -190,15 +192,17 @@ public class PreferencesHelperTest extends UiServiceTestCase { return baos; } - private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception { - loadByteArrayXml(stream.toByteArray(), forRestore); + private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore, int userId) + throws Exception { + loadByteArrayXml(stream.toByteArray(), forRestore, userId); } - private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception { + private void loadByteArrayXml(byte[] byteArray, boolean forRestore, int userId) + throws Exception { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null); parser.nextTag(); - mHelper.readXml(parser, forRestore); + mHelper.readXml(parser, forRestore, userId); } private void compareChannels(NotificationChannel expected, NotificationChannel actual) { @@ -242,6 +246,69 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); } + private void setUpPackageWithUid(String packageName, int uid) throws Exception { + when(mPm.getApplicationInfoAsUser(eq(packageName), anyInt(), anyInt())) + .thenReturn(new ApplicationInfo()); + when(mPm.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid); + } + + @Test + public void testWriteXml_onlyBackupsTargetUser() throws Exception { + // Setup package notifications. + String package0 = "test.package.user0"; + int uid0 = 1001; + setUpPackageWithUid(package0, uid0); + NotificationChannel channel0 = new NotificationChannel("id0", "name0", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(package0, uid0, channel0, true, false); + + String package10 = "test.package.user10"; + int uid10 = 1001001; + setUpPackageWithUid(package10, uid10); + NotificationChannel channel10 = new NotificationChannel("id10", "name10", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(package10, uid10, channel10, true, false); + + ByteArrayOutputStream baos = writeXmlAndPurge(package10, uid10, true, 10); + + // Reset state. + mHelper.onPackagesChanged(true, 0, new String[] {package0}, new int[] {uid0}); + mHelper.onPackagesChanged(true, 10, new String[] {package10}, new int[] {uid10}); + + // Parse backup data. + loadStreamXml(baos, true, 0); + loadStreamXml(baos, true, 10); + + assertEquals( + channel10, + mHelper.getNotificationChannel(package10, uid10, channel10.getId(), false)); + assertNull(mHelper.getNotificationChannel(package0, uid0, channel0.getId(), false)); + } + + @Test + public void testReadXml_onlyRestoresTargetUser() throws Exception { + // Setup package in user 0. + String package0 = "test.package.user0"; + int uid0 = 1001; + setUpPackageWithUid(package0, uid0); + NotificationChannel channel0 = new NotificationChannel("id0", "name0", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(package0, uid0, channel0, true, false); + + ByteArrayOutputStream baos = writeXmlAndPurge(package0, uid0, true, 0); + + // Reset state. + mHelper.onPackagesChanged(true, 0, new String[] {package0}, new int[] {uid0}); + + // Restore should convert the uid according to the target user. + int expectedUid = 1001001; + setUpPackageWithUid(package0, expectedUid); + // Parse backup data. + loadStreamXml(baos, true, 10); + + assertEquals( + channel0, + mHelper.getNotificationChannel(package0, expectedUid, channel0.getId(), false)); + assertNull(mHelper.getNotificationChannel(package0, uid0, channel0.getId(), false)); + } + @Test public void testChannelXml() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); @@ -270,12 +337,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true); mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, channel1.getId(), - channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, + UserHandle.USER_ALL, channel1.getId(), channel2.getId(), + NotificationChannel.DEFAULT_CHANNEL_ID); mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{ UID_N_MR1}); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1)); @@ -337,14 +405,15 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(), - channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + UserHandle.USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(), + NotificationChannel.DEFAULT_CHANNEL_ID); mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1, PKG_O}, new int[]{UID_N_MR1, UID_O}); mHelper.setShowBadge(PKG_O, UID_O, true); - loadStreamXml(baos, true); + loadStreamXml(baos, true, UserHandle.USER_SYSTEM); assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O)); assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); @@ -385,10 +454,11 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId()); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + UserHandle.USER_SYSTEM, channel.getId()); // Testing that in restore we are given the canonical version - loadStreamXml(baos, true); + loadStreamXml(baos, true, UserHandle.USER_SYSTEM); verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI)); } @@ -410,9 +480,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId()); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + UserHandle.USER_SYSTEM, channel.getId()); - loadStreamXml(baos, true); + loadStreamXml(baos, true, UserHandle.USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, channel.getId(), false); @@ -431,9 +502,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId()); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + UserHandle.USER_SYSTEM, channel.getId()); - loadStreamXml(baos, true); + loadStreamXml(baos, true, UserHandle.USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, channel.getId(), false); @@ -460,7 +532,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</package>\n" + "</ranking>\n"; - loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true); + loadByteArrayXml( + backupWithUncanonicalizedSoundUri.getBytes(), true, UserHandle.USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); @@ -472,9 +545,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(null, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId()); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + UserHandle.USER_SYSTEM, channel.getId()); - loadStreamXml(baos, true); + loadStreamXml(baos, true, UserHandle.USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, channel.getId(), false); @@ -504,8 +578,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false)); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(), - channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + UserHandle.USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(), + NotificationChannel.DEFAULT_CHANNEL_ID); mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{ UID_N_MR1}); @@ -513,7 +588,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mHelper.readXml(parser, true); + mHelper.readXml(parser, true, UserHandle.USER_SYSTEM); assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false)); assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false)); @@ -525,9 +600,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, - NotificationChannel.DEFAULT_CHANNEL_ID); + UserHandle.USER_ALL, NotificationChannel.DEFAULT_CHANNEL_ID); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); final NotificationChannel updated = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false); @@ -546,9 +621,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, - NotificationChannel.DEFAULT_CHANNEL_ID); + UserHandle.USER_ALL, NotificationChannel.DEFAULT_CHANNEL_ID); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance()); @@ -568,7 +643,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), null); parser.nextTag(); - mHelper.readXml(parser, false); + mHelper.readXml(parser, false, UserHandle.USER_ALL); final NotificationChannel updated1 = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false); @@ -590,13 +665,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { final NotificationChannel defaultChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false); assertTrue(defaultChannel != null); - ByteArrayOutputStream baos = - writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, NotificationChannel.DEFAULT_CHANNEL_ID); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, + UserHandle.USER_ALL, NotificationChannel.DEFAULT_CHANNEL_ID); // Load package at higher sdk. final ApplicationInfo upgraded = new ApplicationInfo(); upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); // Default Channel should be gone. assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, @@ -608,13 +683,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, - NotificationChannel.DEFAULT_CHANNEL_ID, "bananas"); + UserHandle.USER_ALL, NotificationChannel.DEFAULT_CHANNEL_ID, "bananas"); // Load package at higher sdk. final ApplicationInfo upgraded = new ApplicationInfo(); upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); // Default Channel should be gone. assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, @@ -624,11 +699,11 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, - NotificationChannel.DEFAULT_CHANNEL_ID, "bananas"); + UserHandle.USER_ALL, NotificationChannel.DEFAULT_CHANNEL_ID, "bananas"); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); // Should still have the newly created channel that wasn't in the xml. assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "bananas", false) != null); @@ -1828,6 +1903,46 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testBubblesOverrideTrue() { + Secure.putIntForUser(getContext().getContentResolver(), + Secure.NOTIFICATION_BUBBLES, 1, + USER.getIdentifier()); + mHelper.updateBubblesEnabled(); // would be called by settings observer + assertTrue(mHelper.bubblesEnabled(USER)); + } + + @Test + public void testBubblesOverrideFalse() { + Secure.putIntForUser(getContext().getContentResolver(), + Secure.NOTIFICATION_BUBBLES, 0, + USER.getIdentifier()); + mHelper.updateBubblesEnabled(); // would be called by settings observer + assertFalse(mHelper.bubblesEnabled(USER)); + } + + @Test + public void testBubblesForUserAll() { + try { + mHelper.bubblesEnabled(UserHandle.ALL); + } catch (Exception e) { + fail("just don't throw"); + } + } + + @Test + public void testBubblesOverrideUserIsolation() { + Secure.putIntForUser(getContext().getContentResolver(), + Secure.NOTIFICATION_BUBBLES, 0, + USER.getIdentifier()); + Secure.putIntForUser(getContext().getContentResolver(), + Secure.NOTIFICATION_BUBBLES, 1, + USER2.getIdentifier()); + mHelper.updateBubblesEnabled(); // would be called by settings observer + assertFalse(mHelper.bubblesEnabled(USER)); + assertTrue(mHelper.bubblesEnabled(USER2)); + } + + @Test public void testOnLocaleChanged_updatesDefaultChannels() throws Exception { String newLabel = "bananas!"; final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1, @@ -2007,9 +2122,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testXml_statusBarIcons_default() throws Exception { - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, mHelper.shouldHideSilentStatusIcons()); @@ -2019,9 +2134,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testXml_statusBarIcons() throws Exception { mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, mHelper.shouldHideSilentStatusIcons()); @@ -2115,9 +2230,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDelegateXml_noDelegate() throws Exception { mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); } @@ -2126,9 +2241,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDelegateXml_delegate() throws Exception { mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); } @@ -2138,9 +2253,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53); mHelper.revokeNotificationDelegate(PKG_O, UID_O); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); } @@ -2150,9 +2265,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53); mHelper.toggleNotificationDelegate(PKG_O, UID_O, false); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -2168,9 +2283,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.toggleNotificationDelegate(PKG_O, UID_O, false); mHelper.revokeNotificationDelegate(PKG_O, UID_O); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -2186,9 +2301,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testAllowBubbles_defaults() throws Exception { assertTrue(mHelper.areBubblesAllowed(PKG_O, UID_O)); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertTrue(mHelper.areBubblesAllowed(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); @@ -2201,9 +2316,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); - loadStreamXml(baos, false); + loadStreamXml(baos, false, UserHandle.USER_ALL); assertFalse(mHelper.areBubblesAllowed(PKG_O, UID_O)); assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java index c79e1db0a745..b32288720fd2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java @@ -125,11 +125,8 @@ public class RankingHelperTest extends UiServiceTestCase { InstrumentationRegistry.getContext().getContentResolver()); when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getApplicationInfo()).thenReturn(legacy); - // most tests assume badging is enabled TestableContentResolver contentResolver = getContext().getContentResolver(); contentResolver.setFallbackToExisting(false); - Secure.putIntForUser(contentResolver, - Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID)); ContentProvider testContentProvider = mock(ContentProvider.class); when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index a459b0a26932..08d83333e91b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -25,6 +25,7 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertNotEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -56,6 +57,7 @@ import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.VolumePolicy; import android.net.Uri; +import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.Condition; @@ -158,19 +160,58 @@ public class ZenModeHelperTest extends UiServiceTestCase { return new XmlResourceParserImpl(parser); } - private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup, Integer version) - throws Exception { + private ByteArrayOutputStream writeXmlAndPurge(Integer version) throws Exception { XmlSerializer serializer = new FastXmlSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); - mZenModeHelperSpy.writeXml(serializer, forBackup, version); + mZenModeHelperSpy.writeXml(serializer, false, version, UserHandle.USER_ALL); serializer.endDocument(); serializer.flush(); mZenModeHelperSpy.setConfig(new ZenModeConfig(), null, "writing xml"); return baos; } + private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId) + throws Exception { + XmlSerializer serializer = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mZenModeHelperSpy.writeXml(serializer, true, version, userId); + serializer.endDocument(); + serializer.flush(); + ZenModeConfig newConfig = new ZenModeConfig(); + newConfig.user = userId; + mZenModeHelperSpy.setConfig(newConfig, null, "writing xml"); + return baos; + } + + private XmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + return parser; + } + + private ArrayMap<String, ZenModeConfig.ZenRule> getCustomAutomaticRules() { + ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>(); + ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule(); + final ScheduleInfo customRuleInfo = new ScheduleInfo(); + customRule.enabled = true; + customRule.creationTime = 0; + customRule.id = "customRule"; + customRule.name = "Custom Rule"; + customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo); + customRule.configurationActivity = + new ComponentName("android", "ScheduleConditionProvider"); + customRule.pkg = customRule.configurationActivity.getPackageName(); + automaticRules.put("customRule", customRule); + return automaticRules; + } + @Test public void testZenOff_NoMuteApplied() { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF; @@ -639,50 +680,86 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); - ByteArrayOutputStream baos = writeXmlAndPurge(false, null); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream( - new ByteArrayInputStream(baos.toByteArray())), null); - parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + ByteArrayOutputStream baos = writeXmlAndPurge(null); + XmlPullParser parser = getParserForByteStream(baos); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals("Config mismatch: current vs expected: " + mZenModeHelperSpy.mConfig.diff(expected), expected, mZenModeHelperSpy.mConfig); } @Test - public void testReadXmlRestore() throws Exception { + public void testWriteXml_onlyBackupsTargetUser() throws Exception { + // Setup configs for user 10 and 11. setupZenConfig(); + ZenModeConfig config10 = mZenModeHelperSpy.mConfig.copy(); + config10.user = 10; + config10.allowAlarms = true; + config10.allowMedia = true; + mZenModeHelperSpy.setConfig(config10, null, "writeXml"); + ZenModeConfig config11 = mZenModeHelperSpy.mConfig.copy(); + config11.user = 11; + config11.allowAlarms = false; + config11.allowMedia = false; + mZenModeHelperSpy.setConfig(config11, null, "writeXml"); + + // Backup user 10 and reset values. + ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10); + ZenModeConfig newConfig11 = new ZenModeConfig(); + newConfig11.user = 11; + mZenModeHelperSpy.mConfigs.put(11, newConfig11); + + // Parse backup data. + XmlPullParser parser = getParserForByteStream(baos); + mZenModeHelperSpy.readXml(parser, true, 10); + mZenModeHelperSpy.readXml(parser, true, 11); + + ZenModeConfig actual = mZenModeHelperSpy.mConfigs.get(10); + assertEquals( + "Config mismatch: current vs expected: " + actual.diff(config10), config10, actual); + assertNotEquals("Expected config mismatch", config11, mZenModeHelperSpy.mConfigs.get(11)); + } + @Test + public void testReadXmlRestore_forSystemUser() throws Exception { + setupZenConfig(); // one enabled automatic rule - ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>(); - ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule(); - final ScheduleInfo customRuleInfo = new ScheduleInfo(); - customRule.enabled = true; - customRule.creationTime = 0; - customRule.id = "customRule"; - customRule.name = "Custom Rule"; - customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; - customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo); - customRule.configurationActivity - = new ComponentName("android", "ScheduleConditionProvider"); - customRule.pkg = customRule.configurationActivity.getPackageName(); - automaticRules.put("customRule", customRule); - mZenModeHelperSpy.mConfig.automaticRules = automaticRules; - + mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules(); ZenModeConfig original = mZenModeHelperSpy.mConfig.copy(); - ByteArrayOutputStream baos = writeXmlAndPurge(false, null); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream( - new ByteArrayInputStream(baos.toByteArray())), null); - parser.nextTag(); - mZenModeHelperSpy.readXml(parser, true); + ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); + XmlPullParser parser = getParserForByteStream(baos); + mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM); + assertEquals("Config mismatch: current vs original: " + mZenModeHelperSpy.mConfig.diff(original), original, mZenModeHelperSpy.mConfig); assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode()); } + /** Restore should ignore the data's user id and restore for the target user. */ + @Test + public void testReadXmlRestore_forNonSystemUser() throws Exception { + // Setup config. + setupZenConfig(); + mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules(); + ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); + + // Backup data for user 0. + ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); + + // Restore data for user 10. + XmlPullParser parser = getParserForByteStream(baos); + mZenModeHelperSpy.readXml(parser, true, 10); + + ZenModeConfig actual = mZenModeHelperSpy.mConfigs.get(10); + expected.user = 10; + assertEquals( + "Config mismatch: current vs original: " + actual.diff(expected), expected, actual); + assertEquals(expected.hashCode(), actual.hashCode()); + expected.user = 0; + assertNotEquals(expected, mZenModeHelperSpy.mConfig); + } + @Test public void testWriteXmlWithZenPolicy() throws Exception { final String ruleId = "customRule"; @@ -715,12 +792,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); - ByteArrayOutputStream baos = writeXmlAndPurge(false, null); + ByteArrayOutputStream baos = writeXmlAndPurge(null); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId); ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId); @@ -729,7 +806,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testReadXmlRestoreWithZenPolicy() throws Exception { + public void testReadXmlRestoreWithZenPolicy_forSystemUser() throws Exception { final String ruleId = "customRule"; setupZenConfig(); @@ -756,12 +833,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); - ByteArrayOutputStream baos = writeXmlAndPurge(false, null); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream( - new ByteArrayInputStream(baos.toByteArray())), null); - parser.nextTag(); - mZenModeHelperSpy.readXml(parser, true); + ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); + XmlPullParser parser = getParserForByteStream(baos); + mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM); ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId); ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId); @@ -786,12 +860,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfig.automaticRules = enabledAutoRule; // set previous version - ByteArrayOutputStream baos = writeXmlAndPurge(false, 5); + ByteArrayOutputStream baos = writeXmlAndPurge(5); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertTrue(mZenModeHelperSpy.mConfig.automaticRules.containsKey("customRule")); setupZenConfigMaintained(); @@ -811,7 +885,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals(0, mZenModeHelperSpy.mConfig.suppressedVisualEffects); @@ -827,7 +901,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals(0, mZenModeHelperSpy.mConfig.suppressedVisualEffects); } @@ -846,7 +920,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals(0, mZenModeHelperSpy.mConfig.suppressedVisualEffects); } @@ -865,7 +939,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS @@ -884,7 +958,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals(SUPPRESSED_EFFECT_PEEK, mZenModeHelperSpy.mConfig.suppressedVisualEffects); @@ -900,7 +974,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS, mZenModeHelperSpy.mConfig.suppressedVisualEffects); @@ -915,12 +989,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfig.automaticRules = new ArrayMap<>(); // set previous version - ByteArrayOutputStream baos = writeXmlAndPurge(false, 5); + ByteArrayOutputStream baos = writeXmlAndPurge(5); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); // check default rules ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules; @@ -951,12 +1025,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfig.automaticRules = disabledAutoRule; // set previous version - ByteArrayOutputStream baos = writeXmlAndPurge(false, 5); + ByteArrayOutputStream baos = writeXmlAndPurge(5); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); // check default rules ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules; @@ -1003,12 +1077,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfig.automaticRules = automaticRules; // set previous version - ByteArrayOutputStream baos = writeXmlAndPurge(false, 5); + ByteArrayOutputStream baos = writeXmlAndPurge(5); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); // check default rules ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules; @@ -1072,12 +1146,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfig.automaticRules = automaticRules; // set previous version - ByteArrayOutputStream baos = writeXmlAndPurge(false, 5); + ByteArrayOutputStream baos = writeXmlAndPurge(5); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mZenModeHelperSpy.readXml(parser, false); + mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); // check default rules ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules; diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 3f3b99692e9c..bfda2eac25c9 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -37,8 +37,10 @@ <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.REORDER_TASKS" /> + <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" - android:testOnly="true"> + android:testOnly="true" + android:largeHeap="true"> <uses-library android:name="android.test.mock" android:required="true" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" /> diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index ee228610ab21..2de4ae02828c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -57,7 +57,6 @@ import java.util.Arrays; */ @SmallTest @Presubmit -@FlakyTest(detail="promote once confirmed non-flaky") public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { private ActivityMetricsLogger mActivityMetricsLogger; private ActivityMetricsLaunchObserver mLaunchObserver; 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 192915fe903c..4073ff106592 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -24,6 +24,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; @@ -34,6 +35,9 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -381,6 +385,21 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test + public void testShouldResume_stackVisibility() { + mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); + spyOn(mStack); + + doReturn(STACK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null); + assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */)); + + doReturn(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null); + assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); + + doReturn(STACK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null); + assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); + } + + @Test public void testPushConfigurationWhenLaunchTaskBehind() throws Exception { mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 986943aaf146..822700f48dd5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -37,6 +37,9 @@ import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; @@ -321,12 +324,23 @@ public class ActivityStackTests extends ActivityTestsBase { assertFalse(homeStack.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); // Home stack should be visible if one of the halves of split-screen is translucent. splitScreenPrimary.setIsTranslucent(true); assertTrue(homeStack.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + homeStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); final TestActivityStack splitScreenSecondary2 = createStackForShouldBeVisibleTest(mDefaultDisplay, @@ -336,12 +350,20 @@ public class ActivityStackTests extends ActivityTestsBase { splitScreenSecondary2.setIsTranslucent(false); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenSecondary2.getVisibility(null /* starting */)); // First split-screen secondary should be visible behind another translucent split-screen // secondary. splitScreenSecondary2.setIsTranslucent(true); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenSecondary2.getVisibility(null /* starting */)); final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); @@ -352,6 +374,14 @@ public class ActivityStackTests extends ActivityTestsBase { assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + assistantStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary2.getVisibility(null /* starting */)); // Split-screen stacks should be visible behind a translucent fullscreen stack. assistantStack.setIsTranslucent(true); @@ -359,6 +389,14 @@ public class ActivityStackTests extends ActivityTestsBase { assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + assistantStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + splitScreenSecondary2.getVisibility(null /* starting */)); // Assistant stack shouldn't be visible behind translucent split-screen stack assistantStack.setIsTranslucent(false); @@ -369,6 +407,113 @@ public class ActivityStackTests extends ActivityTestsBase { assertFalse(assistantStack.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + assistantStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenSecondary2.getVisibility(null /* starting */)); + } + + @Test + public void testGetVisibility_FullscreenBehindTranslucent() { + final TestActivityStack bottomStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + final TestActivityStack translucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + bottomStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + translucentStack.getVisibility(null /* starting */)); + } + + @Test + public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() { + final TestActivityStack bottomStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + final TestActivityStack translucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + final TestActivityStack opaqueStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + + assertEquals(STACK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + translucentStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */)); + } + + @Test + public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() { + final TestActivityStack bottomStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + final TestActivityStack opaqueStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + final TestActivityStack translucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + + assertEquals(STACK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + opaqueStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + translucentStack.getVisibility(null /* starting */)); + } + + @Test + public void testGetVisibility_FullscreenTranslucentBehindTranslucent() { + final TestActivityStack bottomTranslucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + final TestActivityStack translucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + bottomTranslucentStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + translucentStack.getVisibility(null /* starting */)); + } + + @Test + public void testGetVisibility_FullscreenTranslucentBehindOpaque() { + final TestActivityStack bottomTranslucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + final TestActivityStack opaqueStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + + assertEquals(STACK_VISIBILITY_INVISIBLE, + bottomTranslucentStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */)); + } + + @Test + public void testGetVisibility_FullscreenBehindTranslucentAndPip() { + final TestActivityStack bottomStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + false /* translucent */); + final TestActivityStack translucentStack = + createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + true /* translucent */); + final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + + assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + bottomStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + translucentStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */)); } @Test @@ -628,6 +773,14 @@ public class ActivityStackTests extends ActivityTestsBase { assertFalse(assistantStack.shouldBeVisible(null /* starting */)); } + private TestActivityStack createStandardStackForVisibilityTest(int windowingMode, + boolean translucent) { + final TestActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay, + windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); + stack.setIsTranslucent(translucent); + return stack; + } + @SuppressWarnings("TypeParameterUnusedInFormals") private <T extends ActivityStack> T createStackForShouldBeVisibleTest( ActivityDisplay display, int windowingMode, int activityType, boolean onTop) { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 3bf884face42..a7520dcbcff9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -16,24 +16,29 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import android.graphics.Rect; import android.os.IBinder; +import android.view.Display; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; -import org.junit.Before; import org.junit.Test; /** @@ -42,7 +47,6 @@ import org.junit.Test; * Build/Install/Run: * atest WmTests:AppChangeTransitionTests */ -@FlakyTest(detail = "Promote when shown to be stable.") @SmallTest public class AppChangeTransitionTests extends WindowTestsBase { @@ -50,14 +54,20 @@ public class AppChangeTransitionTests extends WindowTestsBase { private Task mTask; private WindowTestUtils.TestAppWindowToken mToken; - @Before - public void setUp() throws Exception { - mStack = createTaskStackOnDisplay(mDisplayContent); + public void setUpOnDisplay(DisplayContent dc) { + mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc); mTask = createTaskInStack(mStack, 0 /* userId */); - mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mToken = WindowTestUtils.createTestAppWindowToken(dc); mToken.mSkipOnParentChanged = false; mTask.addChild(mToken, 0); + + // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests. + RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + RemoteAnimationAdapter adapter = + new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false); + definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter); + dc.registerRemoteAnimations(definition); } class TestRemoteAnimationRunner implements IRemoteAnimationRunner { @@ -86,14 +96,58 @@ public class AppChangeTransitionTests extends WindowTestsBase { @Test public void testModeChangeRemoteAnimatorNoSnapshot() { - RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); - RemoteAnimationAdapter adapter = - new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false); - definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter); - mDisplayContent.registerRemoteAnimations(definition); + // setup currently defaults to no snapshot. + setUpOnDisplay(mDisplayContent); mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); assertEquals(1, mDisplayContent.mChangingApps.size()); + + // Verify we are in a change transition, but without a snapshot. + // Though, the test will actually have crashed by now if a snapshot is attempted. + assertNull(mToken.getThumbnail()); + assertTrue(mToken.isInChangeTransition()); + + waitUntilHandlersIdle(); + mToken.removeImmediately(); + } + + @Test + public void testCancelPendingChangeOnRemove() { + // setup currently defaults to no snapshot. + setUpOnDisplay(mDisplayContent); + + mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(1, mDisplayContent.mChangingApps.size()); + assertTrue(mToken.isInChangeTransition()); + + // Removing the app-token from the display should clean-up the + // the change leash. + mDisplayContent.removeAppToken(mToken.token); + assertEquals(0, mDisplayContent.mChangingApps.size()); + assertFalse(mToken.isInChangeTransition()); + + waitUntilHandlersIdle(); + mToken.removeImmediately(); + } + + @Test + public void testNoChangeWhenMoveDisplay() { + mDisplayContent.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); + dc1.setWindowingMode(WINDOWING_MODE_FREEFORM); + setUpOnDisplay(dc1); + + assertEquals(WINDOWING_MODE_FREEFORM, mTask.getWindowingMode()); + + // Reparenting to a display with different windowing mode may trigger + // a change transition internally, but it should be cleaned-up once + // the display change is complete. + mStack.reparent(mDisplayContent.getDisplayId(), new Rect(), true); + + assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode()); + + // Make sure we're not waiting for a change animation (no leash) + assertFalse(mToken.isInChangeTransition()); assertNull(mToken.getThumbnail()); waitUntilHandlersIdle(); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java index ea5ab7bf0621..dd5f32d248b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -87,8 +87,8 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); callbackCaptor.getValue().onAnimationFinished(mSpec); - verify(mTransaction).reparent(eq(leash), eq(null)); - verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null)); + verify(mTransaction).remove(eq(leash)); + verify(mTransaction).remove(eq(animationBoundsLayer)); assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse(); } @@ -100,8 +100,8 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer; mToken.mSurfaceAnimator.cancelAnimation(); - verify(mTransaction).reparent(eq(leash), eq(null)); - verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null)); + verify(mTransaction).remove(eq(leash)); + verify(mTransaction).remove(eq(animationBoundsLayer)); assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index f99cd4b18647..5b32fe68feae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -166,7 +166,7 @@ public class DimmerTests extends WindowTestsBase { mDimmer.updateDims(mTransaction, new Rect()); verify(mTransaction).show(getDimLayer()); - verify(dimLayer, never()).destroy(); + verify(dimLayer, never()).remove(); } @Test @@ -212,7 +212,7 @@ public class DimmerTests extends WindowTestsBase { mDimmer.updateDims(mTransaction, new Rect()); verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any( SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean()); - verify(mHost.getPendingTransaction()).reparent(dimLayer, null); + verify(mHost.getPendingTransaction()).remove(dimLayer); } @Test @@ -228,7 +228,7 @@ public class DimmerTests extends WindowTestsBase { mDimmer.updateDims(mTransaction, new Rect()); verify(mTransaction).show(dimLayer); - verify(dimLayer, never()).destroy(); + verify(dimLayer, never()).remove(); } @Test @@ -269,7 +269,7 @@ public class DimmerTests extends WindowTestsBase { mDimmer.updateDims(mTransaction, new Rect()); verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any( SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean()); - verify(mTransaction).reparent(dimLayer, null); + verify(mTransaction).remove(dimLayer); } private SurfaceControl getDimLayer() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f3994630adee..a62bc713db40 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -51,8 +51,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; import android.annotation.SuppressLint; +import android.app.WindowConfiguration; import android.content.res.Configuration; import android.graphics.Rect; import android.os.SystemClock; @@ -63,6 +65,8 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; +import android.view.ViewRootImpl; +import android.view.test.InsetsModeSession; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; @@ -585,17 +589,15 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testOnDescendantOrientationRequestChanged() { - final DisplayInfo info = new DisplayInfo(); - info.logicalWidth = 1080; - info.logicalHeight = 1920; - info.logicalDensityDpi = 240; - final DisplayContent dc = createNewDisplay(info); - dc.configureDisplayPolicy(); + final DisplayContent dc = createNewDisplay(); mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); + final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); - window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE; + window.mAppToken.setOrientation(newOrientation); ActivityRecord activityRecord = mock(ActivityRecord.class); @@ -606,22 +608,21 @@ public class DisplayContentTests extends WindowTestsBase { verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(), same(activityRecord), anyBoolean(), eq(dc.getDisplayId())); final Configuration newDisplayConfig = captor.getValue(); - assertEquals(Configuration.ORIENTATION_LANDSCAPE, newDisplayConfig.orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation); } @Test public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { - final DisplayInfo info = new DisplayInfo(); - info.logicalWidth = 1080; - info.logicalHeight = 1920; - info.logicalDensityDpi = 240; - final DisplayContent dc = createNewDisplay(info); + final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation(true); mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); + final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); - window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE; + window.mAppToken.setOrientation(newOrientation); ActivityRecord activityRecord = mock(ActivityRecord.class); @@ -632,6 +633,39 @@ public class DisplayContentTests extends WindowTestsBase { eq(activityRecord), anyBoolean(), eq(dc.getDisplayId())); } + @Test + public void testComputeImeParent_app() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); + assertEquals(dc.mInputMethodTarget.mAppToken.getSurfaceControl(), + dc.computeImeParent()); + } + } + + @Test + public void testComputeImeParent_app_notFullscreen() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app"); + dc.mInputMethodTarget.setWindowingMode( + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + assertEquals(dc.getWindowingLayer(), dc.computeImeParent()); + } + } + + @Test + public void testComputeImeParent_noApp() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar"); + assertEquals(dc.getWindowingLayer(), dc.computeImeParent()); + } + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 845a09f44b82..4279c4152836 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -28,6 +28,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -37,6 +39,7 @@ import static org.junit.Assert.assertThat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -350,6 +353,48 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + addWindow(mWindow); + + final int forwardedInsetBottom = 50; + mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), + STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), + STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + } + + @Test + public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING; + addWindow(mWindow); + + final int forwardedInsetBottom = 50; + mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + } + + @Test public void layoutHint_appWindow() { synchronized (mWm.mGlobalLock) { // Initialize DisplayFrames diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index d05711e914c0..198e7ce63f52 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -33,6 +33,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.ContentResolver; @@ -611,6 +612,23 @@ public class DisplayRotationTests { // ======================== // Non-rotation API Tests // ======================== + @Test + public void testRespectsAppRequestedOrientationByDefault() throws Exception { + mBuilder.build(); + + assertTrue("Display rotation should respect app requested orientation by" + + " default.", mTarget.respectAppRequestedOrientation()); + } + + @Test + public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception { + mBuilder.build(); + mTarget.setFixedToUserRotation(true); + + assertFalse("Display rotation shouldn't respect app requested orientation if" + + " fixed to user rotation.", mTarget.respectAppRequestedOrientation()); + } + /** * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget} * according to given parameters. diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index a498a1a9172a..0c363de36328 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -39,7 +39,6 @@ import org.junit.Before; import org.junit.Test; @SmallTest -@FlakyTest(detail = "Promote once confirmed non-flaky") @Presubmit public class InsetsSourceProviderTest extends WindowTestsBase { diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java index beaac8e58686..86bf3dbb6973 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java @@ -40,7 +40,6 @@ import org.mockito.MockitoAnnotations; * atest WmTests:PendingRemoteAnimationRegistryTest */ @SmallTest -@FlakyTest @Presubmit public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase { diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java index 434ba932f8ad..c3d2f33b17dd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java @@ -47,7 +47,6 @@ import java.util.function.Predicate; * atest WmTests:PersisterQueueTests */ @MediumTest -@FlakyTest(detail = "Confirm stable in post-submit before removing") @Presubmit public class PersisterQueueTests implements PersisterQueue.Listener { private static final long INTER_WRITE_DELAY_MS = 50; diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java index 530fd6d7d70e..dad6c952d7ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java @@ -31,7 +31,6 @@ import org.junit.Test; * atest WmTests:SafeActivityOptionsTest */ @MediumTest -@FlakyTest @Presubmit public class SafeActivityOptionsTest { diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java index 9b84215a8f3b..8c32e8cea93c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -96,7 +96,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { callbackCaptor.getValue().onAnimationFinished(mSpec); assertNotAnimating(mAnimatable); assertTrue(mAnimatable.mFinishedCallbackCalled); - verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null)); + verify(mTransaction).remove(eq(mAnimatable.mLeash)); // TODO: Verify reparenting once we use mPendingTransaction to reparent it back } @@ -106,7 +106,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { final SurfaceControl firstLeash = mAnimatable.mLeash; mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */); - verify(mTransaction).reparent(eq(firstLeash), eq(null)); + verify(mTransaction).remove(eq(firstLeash)); assertFalse(mAnimatable.mFinishedCallbackCalled); final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass( @@ -133,7 +133,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { assertNotAnimating(mAnimatable); verify(mSpec).onAnimationCancelled(any()); assertTrue(mAnimatable.mFinishedCallbackCalled); - verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null)); + verify(mTransaction).remove(eq(mAnimatable.mLeash)); } @Test @@ -155,7 +155,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { verifyZeroInteractions(mSpec); assertNotAnimating(mAnimatable); assertTrue(mAnimatable.mFinishedCallbackCalled); - verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null)); + verify(mTransaction).remove(eq(mAnimatable.mLeash)); } @Test @@ -171,15 +171,14 @@ public class SurfaceAnimatorTest extends WindowTestsBase { assertNotAnimating(mAnimatable); assertAnimating(mAnimatable2); assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash); - verify(mTransaction, never()).reparent(eq(leash), eq(null)); + verify(mTransaction, never()).remove(eq(leash)); callbackCaptor.getValue().onAnimationFinished(mSpec); assertNotAnimating(mAnimatable2); assertTrue(mAnimatable2.mFinishedCallbackCalled); - verify(mTransaction).reparent(eq(leash), eq(null)); + verify(mTransaction).remove(eq(leash)); } @Test - @FlakyTest(detail = "Promote once confirmed non-flaky") public void testDeferFinish() { // Start animation @@ -198,7 +197,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { mDeferFinishAnimatable.mEndDeferFinishCallback.run(); assertNotAnimating(mAnimatable2); assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled); - verify(mTransaction).reparent(eq(mDeferFinishAnimatable.mLeash), eq(null)); + verify(mTransaction).remove(eq(mDeferFinishAnimatable.mLeash)); } private void assertAnimating(MyAnimatable animatable) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java index df7bc11663bc..12ed3c28161f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java @@ -41,7 +41,6 @@ import org.junit.Test; * Build/Install/Run: * atest WmTests:TaskPersisterTest */ -@FlakyTest(detail = "Promote to presubmit if stable") @Presubmit public class TaskPersisterTest { private static final String TEST_USER_NAME = "AM-Test-User"; 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 e182c45f009e..bcf9dd218835 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -25,13 +25,6 @@ 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 com.android.dx.mockito.inline.extended.ExtendedMockito.any; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.hamcrest.Matchers.not; @@ -41,6 +34,7 @@ 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.Mockito.mock; import android.app.ActivityManager; import android.content.ComponentName; @@ -53,7 +47,6 @@ import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; import android.util.Xml; import android.view.DisplayInfo; -import android.view.Surface; import androidx.test.filters.MediumTest; @@ -62,6 +55,7 @@ import com.android.server.wm.TaskRecord.TaskRecordFactory; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -284,48 +278,33 @@ public class TaskRecordTests extends ActivityTestsBase { } @Test - public void testUpdatesForcedOrientationInBackground() { - final DisplayInfo info = new DisplayInfo(); - info.logicalWidth = 1920; - info.logicalHeight = 1080; - final ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP); - doCallRealMethod().when(display.mDisplayContent).setDisplayRotation(any()); - display.mDisplayContent.setDisplayRotation(mock(DisplayRotation.class)); - doCallRealMethod().when(display.mDisplayContent).onDescendantOrientationChanged(any(), - any()); - doCallRealMethod().when(display.mDisplayContent).setRotation(anyInt()); - doAnswer(invocation -> { - display.mDisplayContent.setRotation(Surface.ROTATION_0); - return null; - }).when(display.mDisplayContent).updateOrientationFromAppTokens(any(), any(), anyBoolean()); + public void testIgnoresForcedOrientationWhenParentHandles() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + DisplayInfo info = new DisplayInfo(); + info.logicalWidth = fullScreenBounds.width(); + info.logicalHeight = fullScreenBounds.height(); + ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP); - final ActivityStack stack = new StackBuilder(mRootActivityContainer) + display.getRequestedOverrideConfiguration().orientation = + Configuration.ORIENTATION_LANDSCAPE; + display.onRequestedOverrideConfigurationChanged( + display.getRequestedOverrideConfiguration()); + ActivityStack stack = new StackBuilder(mRootActivityContainer) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - final TaskRecord task = stack.getChildAt(0); - final ActivityRecord activity = task.getRootActivity(); - - // Wire up app window token and task. - doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt(), any(), any()); - doCallRealMethod().when(activity.mAppWindowToken).onDescendantOrientationChanged(any(), - any()); - doReturn(task.mTask).when(activity.mAppWindowToken).getParent(); - - // Wire up task and stack. - task.mTask.mTaskRecord = task; - doCallRealMethod().when(task.mTask).onDescendantOrientationChanged(any(), any()); - doReturn(stack.getTaskStack()).when(task.mTask).getParent(); - - // Wire up stack and display content. - doCallRealMethod().when(stack.mTaskStack).onDescendantOrientationChanged(any(), any()); - doReturn(display.mDisplayContent).when(stack.mTaskStack).getParent(); - - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - assertTrue("Bounds of the task should be pillarboxed.", - task.getBounds().width() < task.getBounds().height()); - - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - assertTrue("Bounds of the task should be fullscreen.", - task.getBounds().equals(new Rect(0, 0, 1920, 1080))); + TaskRecord task = stack.getChildAt(0); + ActivityRecord root = task.getTopActivity(); + + final WindowContainer parentWindowContainer = mock(WindowContainer.class); + Mockito.doReturn(parentWindowContainer).when(task.mTask).getParent(); + Mockito.doReturn(true).when(parentWindowContainer) + .handlesOrientationChangeFromDescendant(); + + // Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the + // bounds because its parent says it will handle it at a later time. + setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT); + assertEquals(root, task.getRootActivity()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); + assertEquals(fullScreenBounds, task.getBounds()); } /** Ensures that the alias intent won't have target component resolved. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index d1fe48aa63cd..bfb9193551f2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -28,6 +28,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; +import android.os.PowerManager.WakeReason; import android.os.RemoteException; import android.util.proto.ProtoOutputStream; import android.view.IWindow; @@ -182,11 +183,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void startedWakingUp() { + public void startedWakingUp(@WakeReason int reason) { } @Override - public void finishedWakingUp() { + public void finishedWakingUp(@WakeReason int reason) { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java index de3567ed5018..af8ccc981bae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java @@ -37,7 +37,6 @@ import org.junit.Test; * Build/Install/Run: * atest FrameworksServicesTests:WindowContainerControllerTests */ -@FlakyTest(bugId = 74078662) @SmallTest @Presubmit public class WindowContainerControllerTests extends WindowTestsBase { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 862857560a98..a9a76c2e9506 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -51,6 +51,7 @@ import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; +import org.mockito.Mockito; import java.util.Comparator; @@ -739,6 +740,18 @@ public class WindowContainerTests extends WindowTestsBase { verify(root).onDescendantOrientationChanged(binder, activityRecord); } + @Test + public void testHandlesOrientationChangeFromDescendantProgation() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = spy(builder.build()); + + final TestWindowContainer child = root.addChildWindow(); + assertFalse(child.handlesOrientationChangeFromDescendant()); + + Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(); + assertTrue(child.handlesOrientationChangeFromDescendant()); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index c09cd46a6c04..3eb9085b68f6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90; import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.Surface.ROTATION_0; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -38,6 +39,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.hamcrest.Matchers.is; @@ -48,9 +50,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import android.graphics.Insets; import android.graphics.Matrix; @@ -60,6 +64,7 @@ import android.util.Size; import android.view.DisplayCutout; import android.view.InsetsSource; import android.view.SurfaceControl; +import android.view.ViewRootImpl; import android.view.WindowManager; import androidx.test.filters.FlakyTest; @@ -67,6 +72,9 @@ import androidx.test.filters.SmallTest; import com.android.server.wm.utils.WmDisplayCutout; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.util.LinkedList; @@ -77,10 +85,33 @@ import java.util.LinkedList; * Build/Install/Run: * atest FrameworksServicesTests:WindowStateTests */ -@FlakyTest(bugId = 74078662) @SmallTest @Presubmit public class WindowStateTests extends WindowTestsBase { + private static int sPreviousNewInsetsMode; + + @BeforeClass + public static void setUpOnce() { + // TODO: Make use of SettingsSession when it becomes feasible for this. + sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode; + // To let the insets provider control the insets visibility, the insets mode has to be + // NEW_INSETS_MODE_FULL. + ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL; + } + + @AfterClass + public static void tearDownOnce() { + ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode; + } + + @Before + public void setUp() { + // TODO: Let the insets source with new mode keep the visibility control, and remove this + // setup code. Now mTopFullscreenOpaqueWindowState will take back the control of insets + // visibility. + spyOn(mDisplayContent); + doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded(); + } @Test public void testIsParentWindowHidden() { @@ -263,12 +294,12 @@ public class WindowStateTests extends WindowTestsBase { reset(sPowerManagerWrapper); first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString()); assertTrue(appWindowToken.canTurnScreenOn()); reset(sPowerManagerWrapper); second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); assertFalse(appWindowToken.canTurnScreenOn()); // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON @@ -279,12 +310,12 @@ public class WindowStateTests extends WindowTestsBase { reset(sPowerManagerWrapper); first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); assertFalse(appWindowToken.canTurnScreenOn()); reset(sPowerManagerWrapper); second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString()); assertFalse(appWindowToken.canTurnScreenOn()); // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an @@ -300,11 +331,11 @@ public class WindowStateTests extends WindowTestsBase { reset(sPowerManagerWrapper); firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); reset(sPowerManagerWrapper); secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); } @Test @@ -328,6 +359,7 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(app.canAffectSystemUiFlags()); } + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testVisibleWithInsetsProvider() throws Exception { final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar"); @@ -339,6 +371,7 @@ public class WindowStateTests extends WindowTestsBase { mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app); mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR)); + waitUntilHandlersIdle(); assertFalse(topBar.isVisible()); } @@ -435,6 +468,6 @@ public class WindowStateTests extends WindowTestsBase { root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 3048f1a3487b..d55688665f70 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -39,7 +39,6 @@ import org.junit.Test; * Build/Install/Run: * atest FrameworksServicesTests:WindowTokenTests */ -@FlakyTest(bugId = 74078662) @SmallTest @Presubmit public class WindowTokenTests extends WindowTestsBase { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java index b81a8e72301a..b6b9a861a282 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java @@ -60,7 +60,6 @@ import java.nio.charset.StandardCharsets; * Build/Install/Run: * atest FrameworksServicesTests:WindowTracingTest */ -@FlakyTest(bugId = 74078662) @SmallTest @Presubmit public class WindowTracingTest { diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java index 33f34b465576..05d8237b4da4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java @@ -37,7 +37,6 @@ import org.junit.Test; * atest WmTests:RotationCacheTest */ @SmallTest -@FlakyTest(bugId = 74078662) @Presubmit public class RotationCacheTest { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a6d7ee6d85e2..df2f45512465 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -493,6 +493,8 @@ public class UsageStatsService extends SystemService implements switch (event.mEventType) { case Event.ACTIVITY_RESUMED: synchronized (mVisibleActivities) { + // check if this activity has already been resumed + if (mVisibleActivities.get(event.mInstanceId) != null) break; mVisibleActivities.put(event.mInstanceId, event.getClassName()); try { mAppTimeLimit.noteUsageStart(packageName, userId); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 7cab43233f35..93f758c2a9ad 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1307,13 +1307,15 @@ public class VoiceInteractionManagerService extends SystemService { List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user); + int userId = user.getIdentifier(); if (roleHolders.isEmpty()) { - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.ASSISTANT, ""); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.VOICE_INTERACTION_SERVICE, ""); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user)); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.ASSISTANT, "", userId); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, "", userId); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user), + userId); } else { // Assistant is singleton role String pkg = roleHolders.get(0); @@ -1321,7 +1323,7 @@ public class VoiceInteractionManagerService extends SystemService { // Try to set role holder as VoiceInteractionService List<ResolveInfo> services = mPm.queryIntentServicesAsUser( new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg), - PackageManager.GET_META_DATA, user.getIdentifier()); + PackageManager.GET_META_DATA, userId); for (ResolveInfo resolveInfo : services) { ServiceInfo serviceInfo = resolveInfo.serviceInfo; @@ -1339,12 +1341,14 @@ public class VoiceInteractionManagerService extends SystemService { voiceInteractionServiceInfo.getRecognitionService()) .flattenToShortString(); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.ASSISTANT, serviceComponentName); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.ASSISTANT, serviceComponentName, userId); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName, + userId); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName, + userId); return; } @@ -1352,19 +1356,19 @@ public class VoiceInteractionManagerService extends SystemService { // If no service could be found try to set assist activity final List<ResolveInfo> activities = mPm.queryIntentActivitiesAsUser( new Intent(Intent.ACTION_ASSIST).setPackage(pkg), - PackageManager.MATCH_DEFAULT_ONLY, user.getIdentifier()); + PackageManager.MATCH_DEFAULT_ONLY, userId); for (ResolveInfo resolveInfo : activities) { ActivityInfo activityInfo = resolveInfo.activityInfo; - Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.ASSISTANT, - activityInfo.getComponentName().flattenToShortString()); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.VOICE_INTERACTION_SERVICE, ""); - Settings.Secure.putString(getContext().getContentResolver(), + activityInfo.getComponentName().flattenToShortString(), userId); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, "", userId); + Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE, - getDefaultRecognizer(user)); + getDefaultRecognizer(user), userId); } } } diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 37caeb2044ff..f5b4308a1b50 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -58,8 +58,6 @@ cc_library_static { "util.cc", "layout_validation.cc", ], - // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc - tidy_checks: ["-clang-analyzer-*"], host_supported: true, } diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS new file mode 100644 index 000000000000..e5aead9ddac8 --- /dev/null +++ b/startop/view_compiler/OWNERS @@ -0,0 +1,2 @@ +eholk@google.com +mathieuc@google.com diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 4c1a0dc7f749..6047e8c74e38 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -426,7 +426,7 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct // Some of the registers don't fit in the four bit short form of the invoke // instruction, so we need to do an invoke/range. To do this, we need to // first move all the arguments into contiguous temporary registers. - std::array<Value, kMaxArgs> scratch{GetScratchRegisters<kMaxArgs>()}; + std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>(); const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id()); CHECK(prototype.has_value()); diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java index 818ebd998f50..2fa388fa037e 100644 --- a/telecomm/java/android/telecom/CallScreeningService.java +++ b/telecomm/java/android/telecom/CallScreeningService.java @@ -75,7 +75,7 @@ import java.lang.annotation.RetentionPolicy; * * public void requestRole() { * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE); - * Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING_APP"); + * Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING"); * startActivityForResult(intent, REQUEST_ID); * } * diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 7d1f8ce75919..6382acf0511d 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -19,6 +19,7 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -571,6 +572,7 @@ public abstract class Conference extends Conferenceable { * @return The primary connection. * @hide */ + @TestApi @SystemApi public Connection getPrimaryConnection() { if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java index 20b04ebed6a4..6317770676cd 100644 --- a/telecomm/java/android/telecom/ConferenceParticipant.java +++ b/telecomm/java/android/telecom/ConferenceParticipant.java @@ -19,6 +19,10 @@ package android.telecom; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.PhoneConstants; /** * Parcelable representation of a participant's state in a conference call. @@ -27,6 +31,11 @@ import android.os.Parcelable; public class ConferenceParticipant implements Parcelable { /** + * RFC5767 states that a SIP URI with an unknown number should use an address of + * {@code anonymous@anonymous.invalid}. E.g. the host name is anonymous.invalid. + */ + private static final String ANONYMOUS_INVALID_HOST = "anonymous.invalid"; + /** * The conference participant's handle (e.g., phone number). */ private final Uri mHandle; @@ -50,6 +59,16 @@ public class ConferenceParticipant implements Parcelable { private final int mState; /** + * The connect time of the participant. + */ + private long mConnectTime; + + /** + * The connect elapsed time of the participant. + */ + private long mConnectElapsedTime; + + /** * Creates an instance of {@code ConferenceParticipant}. * * @param handle The conference participant's handle (e.g., phone number). @@ -92,6 +111,54 @@ public class ConferenceParticipant implements Parcelable { } /** + * Determines the number presentation for a conference participant. Per RFC5767, if the host + * name contains {@code anonymous.invalid} we can assume that there is no valid caller ID + * information for the caller, otherwise we'll assume that the URI can be shown. + * + * @return The number presentation. + */ + @VisibleForTesting + public int getParticipantPresentation() { + Uri address = getHandle(); + if (address == null) { + return PhoneConstants.PRESENTATION_RESTRICTED; + } + + String number = address.getSchemeSpecificPart(); + // If no number, bail early and set restricted presentation. + if (TextUtils.isEmpty(number)) { + return PhoneConstants.PRESENTATION_RESTRICTED; + } + // Per RFC3261, the host name portion can also potentially include extra information: + // E.g. sip:anonymous1@anonymous.invalid;legid=1 + // In this case, hostName will be anonymous.invalid and there is an extra parameter for + // legid=1. + // Parameters are optional, and the address (e.g. test@test.com) will always be the first + // part, with any parameters coming afterwards. + String [] hostParts = number.split("[;]"); + String addressPart = hostParts[0]; + + // Get the number portion from the address part. + // This will typically be formatted similar to: 6505551212@test.com + String [] numberParts = addressPart.split("[@]"); + + // If we can't parse the host name out of the URI, then there is probably other data + // present, and is likely a valid SIP URI. + if (numberParts.length != 2) { + return PhoneConstants.PRESENTATION_ALLOWED; + } + String hostName = numberParts[1]; + + // If the hostname portion of the SIP URI is the invalid host string, presentation is + // restricted. + if (hostName.equals(ANONYMOUS_INVALID_HOST)) { + return PhoneConstants.PRESENTATION_RESTRICTED; + } + + return PhoneConstants.PRESENTATION_ALLOWED; + } + + /** * Writes the {@code ConferenceParticipant} to a parcel. * * @param dest The Parcel in which the object should be written. @@ -121,6 +188,10 @@ public class ConferenceParticipant implements Parcelable { sb.append(Log.pii(mEndpoint)); sb.append(" State: "); sb.append(Connection.stateToString(mState)); + sb.append(" ConnectTime: "); + sb.append(getConnectTime()); + sb.append(" ConnectElapsedTime: "); + sb.append(getConnectElapsedTime()); sb.append("]"); return sb.toString(); } @@ -155,4 +226,26 @@ public class ConferenceParticipant implements Parcelable { public int getState() { return mState; } + + /** + * The connect time of the participant to the conference. + */ + public long getConnectTime() { + return mConnectTime; + } + + public void setConnectTime(long connectTime) { + this.mConnectTime = connectTime; + } + + /** + * The connect elpased time of the participant to the conference. + */ + public long getConnectElapsedTime() { + return mConnectElapsedTime; + } + + public void setConnectElapsedTime(long connectElapsedTime) { + mConnectElapsedTime = connectElapsedTime; + } } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 05d5a13092f1..bd0d4ae27800 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -16,10 +16,6 @@ package android.telecom; -import com.android.internal.os.SomeArgs; -import com.android.internal.telecom.IVideoCallback; -import com.android.internal.telecom.IVideoProvider; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -43,6 +39,10 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import android.view.Surface; +import com.android.internal.os.SomeArgs; +import com.android.internal.telecom.IVideoCallback; +import com.android.internal.telecom.IVideoProvider; + import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java index 57ae5d3c05a0..aac0956d4339 100644 --- a/telecomm/java/android/telecom/DefaultDialerManager.java +++ b/telecomm/java/android/telecom/DefaultDialerManager.java @@ -78,7 +78,7 @@ public class DefaultDialerManager { try { RoleManagerCallback.Future cb = new RoleManagerCallback.Future(); context.getSystemService(RoleManager.class).addRoleHolderAsUser( - RoleManager.ROLE_DIALER, packageName, UserHandle.of(user), + RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(user), AsyncTask.THREAD_POOL_EXECUTOR, cb); cb.get(5, TimeUnit.SECONDS); return true; diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java index 1de67a5883e3..5a97c948f31f 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -16,9 +16,9 @@ package android.telecom; +import android.media.ToneGenerator; import android.os.Parcel; import android.os.Parcelable; -import android.media.ToneGenerator; import android.text.TextUtils; import java.util.Objects; @@ -91,6 +91,12 @@ public final class DisconnectCause implements Parcelable { */ public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED"; + /** + * Reason code, which indicates that the conference call is simulating single party conference. + * @hide + */ + public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL"; + private int mDisconnectCode; private CharSequence mDisconnectLabel; private CharSequence mDisconnectDescription; diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index f5f0af7e4666..cbcd40f15583 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -35,7 +35,6 @@ import com.android.internal.os.SomeArgs; import com.android.internal.telecom.IInCallAdapter; import com.android.internal.telecom.IInCallService; -import java.lang.String; import java.util.Collections; import java.util.List; @@ -212,7 +211,7 @@ import java.util.List; * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li> * </ul> * <p> - * Your app should request to fill the role {@code android.app.role.CAR_MODE_DIALER_APP} in order to + * Your app should request to fill the role {@code android.app.role.CAR_MODE_DIALER} in order to * become the default (see <a href="#requestRole">above</a> for how to request your app fills this * role). * @@ -232,7 +231,7 @@ import java.util.List; * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li> * </ul> * <p> - * Your app should request to fill the role {@code android.app.role.CALL_COMPANION_APP} in order to + * Your app should request to fill the role {@code android.app.role.CALL_COMPANION} in order to * become a call companion app (see <a href="#requestRole">above</a> for how to request your app * fills this role). */ diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 0e17a3373a65..e99a289729a4 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -22,6 +22,7 @@ import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.role.RoleManagerCallback; import android.content.ComponentName; @@ -552,6 +553,7 @@ public class TelecomManager { * * @hide */ + @TestApi @SystemApi public static final int TTY_MODE_OFF = 0; @@ -561,6 +563,7 @@ public class TelecomManager { * * @hide */ + @TestApi @SystemApi public static final int TTY_MODE_FULL = 1; @@ -571,6 +574,7 @@ public class TelecomManager { * * @hide */ + @TestApi @SystemApi public static final int TTY_MODE_HCO = 2; @@ -581,6 +585,7 @@ public class TelecomManager { * * @hide */ + @TestApi @SystemApi public static final int TTY_MODE_VCO = 3; @@ -819,6 +824,7 @@ public class TelecomManager { * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @TestApi @SystemApi public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) { try { @@ -1521,6 +1527,7 @@ public class TelecomManager { * @hide */ @SystemApi + @TestApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @TtyMode int getCurrentTtyMode() { try { @@ -1969,6 +1976,7 @@ public class TelecomManager { * @hide */ @SystemApi + @TestApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall() { try { diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 548227067b67..4539ab3dc310 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2118,6 +2118,608 @@ public final class Telephony { } /** + * Columns for the "rcs_*" tables used by {@link android.telephony.ims.RcsMessageStore} classes. + * + * @hide - not meant for public use + */ + public interface RcsColumns { + /** + * The authority for the content provider + */ + String AUTHORITY = "rcs"; + + /** + * The URI to start building upon to use {@link com.android.providers.telephony.RcsProvider} + */ + Uri CONTENT_AND_AUTHORITY = Uri.parse("content://" + AUTHORITY); + + /** + * The value to be used whenever a transaction that expects an integer to be returned + * failed. + */ + int TRANSACTION_FAILED = Integer.MIN_VALUE; + + /** + * The value that denotes a timestamp was not set before (e.g. a message that is not + * delivered yet will not have a DELIVERED_TIMESTAMP) + */ + long TIMESTAMP_NOT_SET = 0; + + /** + * The table that {@link android.telephony.ims.RcsThread} gets persisted to + */ + interface RcsThreadColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsThread}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_THREAD_URI_PART = "thread"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsThread} via the content + * provider. + */ + Uri RCS_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_THREAD_URI_PART); + + /** + * The unique identifier of an {@link android.telephony.ims.RcsThread} + */ + String RCS_THREAD_ID_COLUMN = "rcs_thread_id"; + } + + /** + * The table that {@link android.telephony.ims.Rcs1To1Thread} gets persisted to + */ + interface Rcs1To1ThreadColumns extends RcsThreadColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.Rcs1To1Thread}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_1_TO_1_THREAD_URI_PART = "p2p_thread"; + + /** + * The URI to query or modify {@link android.telephony.ims.Rcs1To1Thread}s via the + * content provider + */ + Uri RCS_1_TO_1_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_1_TO_1_THREAD_URI_PART); + + /** + * The SMS/MMS thread to fallback to in case of an RCS outage + */ + String FALLBACK_THREAD_ID_COLUMN = "rcs_fallback_thread_id"; + } + + /** + * The table that {@link android.telephony.ims.RcsGroupThread} gets persisted to + */ + interface RcsGroupThreadColumns extends RcsThreadColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsGroupThread}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_GROUP_THREAD_URI_PART = "group_thread"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsGroupThread}s via the + * content provider + */ + Uri RCS_GROUP_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_GROUP_THREAD_URI_PART); + + /** + * The owner/admin of the {@link android.telephony.ims.RcsGroupThread} + */ + String OWNER_PARTICIPANT_COLUMN = "owner_participant"; + + /** + * The user visible name of the group + */ + String GROUP_NAME_COLUMN = "group_name"; + + /** + * The user visible icon of the group + */ + String GROUP_ICON_COLUMN = "group_icon"; + + /** + * The RCS conference URI for this group + */ + String CONFERENCE_URI_COLUMN = "conference_uri"; + } + + /** + * The view that enables polling from all types of RCS threads at once + */ + interface RcsUnifiedThreadColumns extends RcsThreadColumns, Rcs1To1ThreadColumns, + RcsGroupThreadColumns { + /** + * The type of this {@link android.telephony.ims.RcsThread} + */ + String THREAD_TYPE_COLUMN = "thread_type"; + + /** + * Integer returned as a result from a database query that denotes the thread is 1 to 1 + */ + int THREAD_TYPE_1_TO_1 = 0; + + /** + * Integer returned as a result from a database query that denotes the thread is 1 to 1 + */ + int THREAD_TYPE_GROUP = 1; + } + + /** + * The table that {@link android.telephony.ims.RcsParticipant} gets persisted to + */ + interface RcsParticipantColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsParticipant}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_PARTICIPANT_URI_PART = "participant"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsParticipant}s via the + * content provider + */ + Uri RCS_PARTICIPANT_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_PARTICIPANT_URI_PART); + + /** + * The unique identifier of the entry in the database + */ + String RCS_PARTICIPANT_ID_COLUMN = "rcs_participant_id"; + + /** + * A foreign key on canonical_address table, also used by SMS/MMS + */ + String CANONICAL_ADDRESS_ID_COLUMN = "canonical_address_id"; + + /** + * The user visible RCS alias for this participant. + */ + String RCS_ALIAS_COLUMN = "rcs_alias"; + } + + /** + * Additional constants to enable access to {@link android.telephony.ims.RcsParticipant} + * related data + */ + interface RcsParticipantHelpers extends RcsParticipantColumns { + /** + * The view that unifies "rcs_participant" and "canonical_addresses" tables for easy + * access to participant address. + */ + String RCS_PARTICIPANT_WITH_ADDRESS_VIEW = "rcs_participant_with_address_view"; + + /** + * The view that unifies "rcs_participant", "canonical_addresses" and + * "rcs_thread_participant" junction table to get full information on participants that + * contribute to threads. + */ + String RCS_PARTICIPANT_WITH_THREAD_VIEW = "rcs_participant_with_thread_view"; + } + + /** + * The table that {@link android.telephony.ims.RcsMessage} gets persisted to + */ + interface RcsMessageColumns { + /** + * Denotes the type of this message (i.e. + * {@link android.telephony.ims.RcsIncomingMessage} or + * {@link android.telephony.ims.RcsOutgoingMessage} + */ + String MESSAGE_TYPE_COLUMN = "rcs_message_type"; + + /** + * The unique identifier for the message in the database - i.e. the primary key. + */ + String MESSAGE_ID_COLUMN = "rcs_message_row_id"; + + /** + * The globally unique RCS identifier for the message. Please see 4.4.5.2 - GSMA + * RCC.53 (RCS Device API 1.6 Specification) + */ + String GLOBAL_ID_COLUMN = "rcs_message_global_id"; + + /** + * The subscription where this message was sent from/to. + */ + String SUB_ID_COLUMN = "sub_id"; + + /** + * The sending status of the message. + * @see android.telephony.ims.RcsMessage.RcsMessageStatus + */ + String STATUS_COLUMN = "status"; + + /** + * The creation timestamp of the message. + */ + String ORIGINATION_TIMESTAMP_COLUMN = "origination_timestamp"; + + /** + * The text content of the message. + */ + String MESSAGE_TEXT_COLUMN = "rcs_text"; + + /** + * The latitude content of the message, if it contains a location. + */ + String LATITUDE_COLUMN = "latitude"; + + /** + * The longitude content of the message, if it contains a location. + */ + String LONGITUDE_COLUMN = "longitude"; + } + + /** + * The table that additional information of {@link android.telephony.ims.RcsIncomingMessage} + * gets persisted to. + */ + interface RcsIncomingMessageColumns extends RcsMessageColumns { + /** + The path that should be used for referring to + * {@link android.telephony.ims.RcsIncomingMessage}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String INCOMING_MESSAGE_URI_PART = "incoming_message"; + + /** + * The URI to query incoming messages through + * {@link com.android.providers.telephony.RcsProvider} + */ + Uri INCOMING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + INCOMING_MESSAGE_URI_PART); + + /** + * The ID of the {@link android.telephony.ims.RcsParticipant} that sent this message + */ + String SENDER_PARTICIPANT_ID_COLUMN = "sender_participant"; + + /** + * The timestamp of arrival for this message. + */ + String ARRIVAL_TIMESTAMP_COLUMN = "arrival_timestamp"; + + /** + * The time when the recipient has read this message. + */ + String SEEN_TIMESTAMP_COLUMN = "seen_timestamp"; + } + + /** + * The table that additional information of {@link android.telephony.ims.RcsOutgoingMessage} + * gets persisted to. + */ + interface RcsOutgoingMessageColumns extends RcsMessageColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsOutgoingMessage}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String OUTGOING_MESSAGE_URI_PART = "outgoing_message"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsOutgoingMessage}s via the + * content provider + */ + Uri OUTGOING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + OUTGOING_MESSAGE_URI_PART); + } + + /** + * The delivery information of an {@link android.telephony.ims.RcsOutgoingMessage} + */ + interface RcsMessageDeliveryColumns extends RcsOutgoingMessageColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsOutgoingMessageDelivery}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String DELIVERY_URI_PART = "delivery"; + + /** + * The timestamp of delivery of this message. + */ + String DELIVERED_TIMESTAMP_COLUMN = "delivered_timestamp"; + + /** + * The time when the recipient has read this message. + */ + String SEEN_TIMESTAMP_COLUMN = "seen_timestamp"; + } + + /** + * The views that allow querying {@link android.telephony.ims.RcsIncomingMessage} and + * {@link android.telephony.ims.RcsOutgoingMessage} at the same time. + */ + interface RcsUnifiedMessageColumns extends RcsIncomingMessageColumns, + RcsOutgoingMessageColumns { + /** + * The path that is used to query all {@link android.telephony.ims.RcsMessage} in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String UNIFIED_MESSAGE_URI_PART = "message"; + + /** + * The URI to query all types of {@link android.telephony.ims.RcsMessage}s + */ + Uri UNIFIED_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + UNIFIED_MESSAGE_URI_PART); + + /** + * The name of the view that unites rcs_message and rcs_incoming_message tables. + */ + String UNIFIED_INCOMING_MESSAGE_VIEW = "unified_incoming_message_view"; + + /** + * The name of the view that unites rcs_message and rcs_outgoing_message tables. + */ + String UNIFIED_OUTGOING_MESSAGE_VIEW = "unified_outgoing_message_view"; + + /** + * The column that shows from which table the message entry came from. + */ + String MESSAGE_TYPE_COLUMN = "message_type"; + + /** + * Integer returned as a result from a database query that denotes that the message is + * an incoming message + */ + int MESSAGE_TYPE_INCOMING = 1; + + /** + * Integer returned as a result from a database query that denotes that the message is + * an outgoing message + */ + int MESSAGE_TYPE_OUTGOING = 0; + } + + /** + * The table that {@link android.telephony.ims.RcsFileTransferPart} gets persisted to. + */ + interface RcsFileTransferColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsFileTransferPart}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String FILE_TRANSFER_URI_PART = "file_transfer"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsFileTransferPart}s via the + * content provider + */ + Uri FILE_TRANSFER_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + FILE_TRANSFER_URI_PART); + + /** + * The globally unique file transfer ID for this RCS file transfer. + */ + String FILE_TRANSFER_ID_COLUMN = "rcs_file_transfer_id"; + + /** + * The RCS session ID for this file transfer. The ID is implementation dependent but + * should be unique. + */ + String SESSION_ID_COLUMN = "session_id"; + + /** + * The URI that points to the content of this file transfer + */ + String CONTENT_URI_COLUMN = "content_uri"; + + /** + * The file type of this file transfer in bytes. The validity of types is not enforced + * in {@link android.telephony.ims.RcsMessageStore} APIs. + */ + String CONTENT_TYPE_COLUMN = "content_type"; + + /** + * The size of the file transfer in bytes. + */ + String FILE_SIZE_COLUMN = "file_size"; + + /** + * Number of bytes that was successfully transmitted for this file transfer + */ + String SUCCESSFULLY_TRANSFERRED_BYTES = "transfer_offset"; + + /** + * The status of this file transfer + * @see android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus + */ + String TRANSFER_STATUS_COLUMN = "transfer_status"; + + /** + * The on-screen width of the file transfer, if it contains multi-media + */ + String WIDTH_COLUMN = "width"; + + /** + * The on-screen height of the file transfer, if it contains multi-media + */ + String HEIGHT_COLUMN = "height"; + + /** + * The duration of the content in milliseconds if this file transfer contains + * multi-media + */ + String DURATION_MILLIS_COLUMN = "duration"; + + /** + * The URI to the preview of the content of this file transfer + */ + String PREVIEW_URI_COLUMN = "preview_uri"; + + /** + * The type of the preview of the content of this file transfer. The validity of types + * is not enforced in {@link android.telephony.ims.RcsMessageStore} APIs. + */ + String PREVIEW_TYPE_COLUMN = "preview_type"; + } + + /** + * The table that holds the information for + * {@link android.telephony.ims.RcsGroupThreadEvent} and its subclasses. + */ + interface RcsThreadEventColumns { + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to participant joined events (example URI: + * {@code content://rcs/group_thread/3/participant_joined_event}) + */ + String PARTICIPANT_JOINED_URI_PART = "participant_joined_event"; + + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to participant left events. (example URI: + * {@code content://rcs/group_thread/3/participant_left_event/4}) + */ + String PARTICIPANT_LEFT_URI_PART = "participant_left_event"; + + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to name changed events. (example URI: + * {@code content://rcs/group_thread/3/name_changed_event}) + */ + String NAME_CHANGED_URI_PART = "name_changed_event"; + + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to icon changed events. (example URI: + * {@code content://rcs/group_thread/3/icon_changed_event}) + */ + String ICON_CHANGED_URI_PART = "icon_changed_event"; + + /** + * The unique ID of this event in the database, i.e. the primary key + */ + String EVENT_ID_COLUMN = "event_id"; + + /** + * The type of this event + * + * @see RcsEventTypes + */ + String EVENT_TYPE_COLUMN = "event_type"; + + /** + * The timestamp in milliseconds of when this event happened + */ + String TIMESTAMP_COLUMN = "origination_timestamp"; + + /** + * The participant that generated this event + */ + String SOURCE_PARTICIPANT_ID_COLUMN = "source_participant"; + + /** + * The receiving participant of this event if this was an + * {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} or + * {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent} + */ + String DESTINATION_PARTICIPANT_ID_COLUMN = "destination_participant"; + + /** + * The URI for the new icon of the group thread if this was an + * {@link android.telephony.ims.RcsGroupThreadIconChangedEvent} + */ + String NEW_ICON_URI_COLUMN = "new_icon_uri"; + + /** + * The URI for the new name of the group thread if this was an + * {@link android.telephony.ims.RcsGroupThreadNameChangedEvent} + */ + String NEW_NAME_COLUMN = "new_name"; + } + + /** + * The table that {@link android.telephony.ims.RcsParticipantAliasChangedEvent} gets + * persisted to + */ + interface RcsParticipantEventColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsParticipantAliasChangedEvent}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String ALIAS_CHANGE_EVENT_URI_PART = "alias_change_event"; + + /** + * The new alias of the participant + */ + String NEW_ALIAS_COLUMN = "new_alias"; + } + + /** + * These values are used in {@link com.android.providers.telephony.RcsProvider} to determine + * what kind of event is present in the storage. + */ + interface RcsEventTypes { + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsParticipantAliasChangedEvent} + */ + int PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE = 1; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} + */ + int PARTICIPANT_JOINED_EVENT_TYPE = 2; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent} + */ + int PARTICIPANT_LEFT_EVENT_TYPE = 4; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadIconChangedEvent} + */ + int ICON_CHANGED_EVENT_TYPE = 8; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadNameChangedEvent} + */ + int NAME_CHANGED_EVENT_TYPE = 16; + } + + /** + * The view that allows unified querying across all events + */ + interface RcsUnifiedEventHelper extends RcsParticipantEventColumns, RcsThreadEventColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsEvent}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_EVENT_QUERY_URI_PATH = "event"; + + /** + * The URI to query {@link android.telephony.ims.RcsEvent}s via the content provider. + */ + Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_EVENT_QUERY_URI_PATH); + } + } + + /** * Contains all MMS messages. */ public static final class Mms implements BaseMmsColumns { diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index 9c64cf6ddd8f..75165afe097e 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -54,8 +54,15 @@ public final class AccessNetworkConstants { */ @SystemApi public static final class TransportType { + /** + * Invalid transport type. + * @hide + */ + public static final int INVALID = -1; + /** Wireless Wide Area Networks (i.e. Cellular) */ public static final int WWAN = 1; + /** Wireless Local Area Networks (i.e. Wifi) */ public static final int WLAN = 2; @@ -65,6 +72,7 @@ public final class AccessNetworkConstants { /** @hide */ public static String toString(int type) { switch (type) { + case INVALID: return "INVALID"; case WWAN: return "WWAN"; case WLAN: return "WLAN"; default: return Integer.toString(type); diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 30e641d61143..a4207c99ce4d 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -63,6 +63,10 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) { // Convert from HAL values as part of construction. this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance); + + if (mRssi == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java index 6f52b853d23b..5ae89b0f8b3d 100644 --- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java @@ -72,6 +72,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen // Convert from HAL values as part of construction. this(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, tdscdma.rscp != CellInfo.UNAVAILABLE ? -tdscdma.rscp : tdscdma.rscp); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ @@ -79,6 +83,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen // Convert from HAL values as part of construction. this(getRssiDbmFromAsu(tdscdma.signalStrength), tdscdma.bitErrorRate, getRscpDbmFromAsu(tdscdma.rscp)); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java index 0760407171ae..efa3647f0e9b 100644 --- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -92,8 +92,12 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements /** @hide */ public CellSignalStrengthWcdma(android.hardware.radio.V1_0.WcdmaSignalStrength wcdma) { // Convert from HAL values as part of construction. - this(getRssiDbmFromAsu(wcdma.signalStrength), - wcdma.bitErrorRate, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE); + this(getRssiDbmFromAsu(wcdma.signalStrength), wcdma.bitErrorRate, + CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ @@ -103,6 +107,10 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements wcdma.base.bitErrorRate, getRscpDbmFromAsu(wcdma.rscp), getEcNoDbFromAsu(wcdma.ecno)); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index e40bae18d4f0..099015fd3d0e 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -187,15 +187,7 @@ public class SmsMessage { int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); String format = (PHONE_TYPE_CDMA == activePhone) ? SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP; - message = createFromPdu(pdu, format); - - if (null == message || null == message.mWrappedSmsMessage) { - // decoding pdu failed based on activePhone type, must be other format - format = (PHONE_TYPE_CDMA == activePhone) ? - SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2; - message = createFromPdu(pdu, format); - } - return message; + return createFromPdu(pdu, format); } /** @@ -211,11 +203,18 @@ public class SmsMessage { * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent */ public static SmsMessage createFromPdu(byte[] pdu, String format) { - SmsMessageBase wrappedMessage; + return createFromPdu(pdu, format, true); + } + + private static SmsMessage createFromPdu(byte[] pdu, String format, + boolean fallbackToOtherFormat) { if (pdu == null) { Rlog.i(LOG_TAG, "createFromPdu(): pdu is null"); return null; } + SmsMessageBase wrappedMessage; + String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP : + SmsConstants.FORMAT_3GPP2; if (SmsConstants.FORMAT_3GPP2.equals(format)) { wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu); } else if (SmsConstants.FORMAT_3GPP.equals(format)) { @@ -228,8 +227,12 @@ public class SmsMessage { if (wrappedMessage != null) { return new SmsMessage(wrappedMessage); } else { - Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null"); - return null; + if (fallbackToOtherFormat) { + return createFromPdu(pdu, otherFormat, false); + } else { + Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null"); + return null; + } } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index dfe36efcd5d1..313146d5538b 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2047,6 +2047,8 @@ public class SubscriptionManager { putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]); } else { logd("putPhoneIdAndSubIdExtra: no valid subs"); + intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); + intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 739589412f0b..f5d452e9721d 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1326,13 +1326,15 @@ public class TelephonyManager { "android.intent.action.DATA_STALL_DETECTED"; /** - * A service action that identifies a {@link android.app.SmsAppService} subclass in the + * A service action that identifies + * a {@link android.service.carrier.CarrierMessagingClientService} subclass in the * AndroidManifest.xml. * - * <p>See {@link android.app.SmsAppService} for the details. + * <p>See {@link android.service.carrier.CarrierMessagingClientService} for the details. */ @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE"; + public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = + "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; /** * An int extra used with {@link #ACTION_DATA_STALL_DETECTED} to indicate the @@ -4918,12 +4920,13 @@ public class TelephonyManager { * * <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no * longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps - * will receive the latest cached results. Apps targeting + * will receive the latest cached results, which may not be current. Apps targeting * {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated * CellInfo should call - * {android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()} and - * listen for responses via {@link android.telephony.PhoneStateListener#onCellInfoChanged - * onCellInfoChanged()}. + * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}; + * however, in all cases, updates will be rate-limited and are not guaranteed. To determine the + * recency of CellInfo data, callers should check + * {@link android.telephony.CellInfo#getTimeStamp CellInfo#getTimeStamp()}. * * <p>This method returns valid data for devices with * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java index 709b3aa0f804..3255f8d0786d 100644 --- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java +++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java @@ -15,42 +15,72 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.NonNull; +import android.annotation.WorkerThread; /** * Rcs1To1Thread represents a single RCS conversation thread with a total of two - * {@link RcsParticipant}s. - * @hide - TODO(sahinc) make this public + * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal + * Profile Service Definition Document) + * + * @hide - TODO: make public */ public class Rcs1To1Thread extends RcsThread { + private int mThreadId; + + /** + * Public constructor only for RcsMessageStoreController to initialize new threads. + * + * @hide + */ public Rcs1To1Thread(int threadId) { super(threadId); + mThreadId = threadId; } - public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() { - @Override - public Rcs1To1Thread createFromParcel(Parcel in) { - return new Rcs1To1Thread(in); - } - - @Override - public Rcs1To1Thread[] newArray(int size) { - return new Rcs1To1Thread[size]; - } - }; + /** + * @return Returns {@code false} as this is always a 1 to 1 thread. + */ + @Override + public boolean isGroup() { + return false; + } - protected Rcs1To1Thread(Parcel in) { - super(in); + /** + * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the + * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback + * thread. + * + * @return The thread id to be used to query the mms-sms authority + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getFallbackThreadId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadFallbackThreadId(mThreadId)); } - @Override - public int describeContents() { - return 0; + /** + * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the + * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}. + * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function + * also updates the storage. + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.set1To1ThreadFallbackThreadId(mThreadId, fallbackThreadId)); } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(RCS_1_TO_1_TYPE); - super.writeToParcel(dest, flags); + /** + * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @NonNull + @WorkerThread + public RcsParticipant getRecipient() throws RcsMessageStoreException { + return new RcsParticipant( + RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadOtherParticipantId(mThreadId))); } } diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java new file mode 100644 index 000000000000..5512c4c7b19d --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsControllerCall.java @@ -0,0 +1,64 @@ +/* + * 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.telephony.ims; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.telephony.ims.aidl.IRcs; + +/** + * A wrapper class around RPC calls that {@link RcsMessageStore} APIs to minimize boilerplate code. + * + * @hide - not meant for public use + */ +class RcsControllerCall { + static <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException { + IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE)); + if (iRcs == null) { + throw new RcsMessageStoreException("Could not connect to RCS storage service"); + } + + try { + return serviceCall.methodOnIRcs(iRcs); + } catch (RemoteException exception) { + throw new RcsMessageStoreException(exception.getMessage()); + } + } + + static void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall) + throws RcsMessageStoreException { + IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE)); + if (iRcs == null) { + throw new RcsMessageStoreException("Could not connect to RCS storage service"); + } + + try { + serviceCall.methodOnIRcs(iRcs); + } catch (RemoteException exception) { + throw new RcsMessageStoreException(exception.getMessage()); + } + } + + interface RcsServiceCall<R> { + R methodOnIRcs(IRcs iRcs) throws RemoteException; + } + + interface RcsServiceCallWithNoReturn { + void methodOnIRcs(IRcs iRcs) throws RemoteException; + } +} diff --git a/telephony/java/android/telephony/ims/RcsPart.aidl b/telephony/java/android/telephony/ims/RcsEvent.aidl index 8b8077d57676..08974e0a771c 100644 --- a/telephony/java/android/telephony/ims/RcsPart.aidl +++ b/telephony/java/android/telephony/ims/RcsEvent.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsPart; +parcelable RcsEvent; diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java new file mode 100644 index 000000000000..ef359a124d47 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsEvent.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.os.Parcel; + +/** + * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s. + * + * @hide - TODO: make public + */ +public abstract class RcsEvent { + /** + * @hide + */ + protected final long mTimestamp; + + protected RcsEvent(long timestamp) { + mTimestamp = timestamp; + } + + /** + * @return Returns the time of when this event happened. The timestamp is defined as + * milliseconds passed after midnight, January 1, 1970 UTC + */ + public long getTimestamp() { + return mTimestamp; + } + + /** + * Persists the event to the data store + * + * @hide + */ + abstract void persist() throws RcsMessageStoreException; + + /** + * @hide + */ + RcsEvent(Parcel in) { + mTimestamp = in.readLong(); + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mTimestamp); + } +} diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl index 6552a82c9072..f18c4dfd2dcd 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl +++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsIncomingMessage; +parcelable RcsEventQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java new file mode 100644 index 000000000000..5249becd476e --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java @@ -0,0 +1,321 @@ +/* + * 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.telephony.ims; + +import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; + +/** + * The parameters to pass into + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a + * subset of {@link RcsEvent}s present in the message store. + * + * @hide - TODO: make public + */ +public final class RcsEventQueryParams implements Parcelable { + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return all types of + * {@link RcsEvent}s + */ + public static final int ALL_EVENTS = -1; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return sub-types of + * {@link RcsGroupThreadEvent}s + */ + public static final int ALL_GROUP_THREAD_EVENTS = 0; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsParticipantAliasChangedEvent}s + */ + public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = + PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadParticipantJoinedEvent}s + */ + public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = + PARTICIPANT_JOINED_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadParticipantLeftEvent}s + */ + public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = + PARTICIPANT_LEFT_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadNameChangedEvent}s + */ + public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadIconChangedEvent}s + */ + public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT, + GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT, + GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT}) + public @interface EventType { + } + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted + * in the order of creation for faster query results. + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted + * with respect to {@link RcsEvent#getTimestamp()} + */ + public static final int SORT_BY_TIMESTAMP = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP}) + public @interface SortingProperty { + } + + /** + * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle) + * @hide - not meant for public use + */ + public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters"; + + // Which types of events the results should be limited to + private @EventType int mEventType; + // The property which the results should be sorted against + private int mSortingProperty; + // Whether the results should be sorted in ascending order + private boolean mIsAscending; + // The number of results that should be returned with this query + private int mLimit; + // The thread that the results are limited to + private int mThreadId; + + RcsEventQueryParams(@EventType int eventType, int threadId, + @SortingProperty int sortingProperty, boolean isAscending, int limit) { + mEventType = eventType; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + mLimit = limit; + mThreadId = threadId; + } + + /** + * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is + * set to query for. + */ + public @EventType int getEventType() { + return mEventType; + } + + /** + * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is + * set to query for. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property where the results should be sorted against. + * @see SortingProperty + */ + public int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this + * API exposes an ID, it should stay hidden. + * + * @hide + */ + public int getThreadId() { + return mThreadId; + } + + /** + * A helper class to build the {@link RcsEventQueryParams}. + */ + public static class Builder { + private @EventType int mEventType; + private @SortingProperty int mSortingProperty; + private boolean mIsAscending; + private int mLimit = 100; + private int mThreadId; + + /** + * Creates a new builder for {@link RcsEventQueryParams} to be used in + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} + */ + public Builder() { + // empty implementation + } + + /** + * Desired number of events to be returned from the query. Passing in 0 will return all + * existing events at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the type of events to be returned from the query. + * + * @param eventType The type of event to be returned. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setEventType(@EventType int eventType) { + mEventType = eventType; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link RcsEventQueryParams.SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty against which property the results should be sorted + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents + * returning any instances of {@link RcsParticipantAliasChangedEvent}. + * + * @param groupThread The thread to limit the results to. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setGroupThread(@NonNull RcsGroupThread groupThread) { + mThreadId = groupThread.getThreadId(); + return this; + } + + /** + * Builds the {@link RcsEventQueryParams} to use in + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} + * + * @return An instance of {@link RcsEventQueryParams} to use with the event query. + */ + public RcsEventQueryParams build() { + return new RcsEventQueryParams(mEventType, mThreadId, mSortingProperty, + mIsAscending, mLimit); + } + } + + private RcsEventQueryParams(Parcel in) { + mEventType = in.readInt(); + mThreadId = in.readInt(); + mSortingProperty = in.readInt(); + mIsAscending = in.readBoolean(); + mLimit = in.readInt(); + } + + public static final Creator<RcsEventQueryParams> CREATOR = + new Creator<RcsEventQueryParams>() { + @Override + public RcsEventQueryParams createFromParcel(Parcel in) { + return new RcsEventQueryParams(in); + } + + @Override + public RcsEventQueryParams[] newArray(int size) { + return new RcsEventQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mEventType); + dest.writeInt(mThreadId); + dest.writeInt(mSortingProperty); + dest.writeBoolean(mIsAscending); + dest.writeInt(mLimit); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl index c4ce5299e512..7d133350973c 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThread.aidl +++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsGroupThread; +parcelable RcsEventQueryResult; diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java new file mode 100644 index 000000000000..f8d57fa5c09d --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java @@ -0,0 +1,91 @@ +/* + * 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.telephony.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} + * call. This class allows getting the token for querying the next batch of events in order to + * prevent handling large amounts of data at once. + * + * @hide - TODO: make public + */ +public final class RcsEventQueryResult implements Parcelable { + private RcsQueryContinuationToken mContinuationToken; + private List<RcsEvent> mEvents; + + /** + * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create query results + * + * @hide + */ + public RcsEventQueryResult( + RcsQueryContinuationToken continuationToken, + List<RcsEvent> events) { + mContinuationToken = continuationToken; + mEvents = events; + } + + /** + * Returns a token to call + * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} + * to get the next batch of {@link RcsEvent}s. + */ + public RcsQueryContinuationToken getContinuationToken() { + return mContinuationToken; + } + + /** + * Returns all the {@link RcsEvent}s in the current query result. Call {@link + * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch + * of {@link RcsEvent}s. + */ + public List<RcsEvent> getEvents() { + return mEvents; + } + + private RcsEventQueryResult(Parcel in) { + mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader()); + } + + public static final Creator<RcsEventQueryResult> CREATOR = new Creator<RcsEventQueryResult>() { + @Override + public RcsEventQueryResult createFromParcel(Parcel in) { + return new RcsEventQueryResult(in); + } + + @Override + public RcsEventQueryResult[] newArray(int size) { + return new RcsEventQueryResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mContinuationToken, flags); + } +} diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl new file mode 100644 index 000000000000..155219038d7b --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsFileTransferCreationParams; diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java new file mode 100644 index 000000000000..663def5df50f --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java @@ -0,0 +1,360 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Pass an instance of this class to + * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an + * {@link RcsFileTransferPart} and save it into storage. + * + * @hide - TODO: make public + */ +public final class RcsFileTransferCreationParams implements Parcelable { + private String mRcsFileTransferSessionId; + private Uri mContentUri; + private String mContentMimeType; + private long mFileSize; + private long mTransferOffset; + private int mWidth; + private int mHeight; + private long mMediaDuration; + private Uri mPreviewUri; + private String mPreviewMimeType; + private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus; + + /** + * @return Returns the globally unique RCS file transfer session ID for the + * {@link RcsFileTransferPart} to be created + */ + public String getRcsFileTransferSessionId() { + return mRcsFileTransferSessionId; + } + + /** + * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created + */ + public Uri getContentUri() { + return mContentUri; + } + + /** + * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be + * created + */ + public String getContentMimeType() { + return mContentMimeType; + } + + /** + * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created + */ + public long getFileSize() { + return mFileSize; + } + + /** + * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The + * file transfer offset is defined as how many bytes have been successfully transferred to the + * receiver of this file transfer. + */ + public long getTransferOffset() { + return mTransferOffset; + } + + /** + * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in + * pixels. + */ + public int getWidth() { + return mWidth; + } + + /** + * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in + * pixels. + */ + public int getHeight() { + return mHeight; + } + + /** + * @return Returns the duration of the {@link RcsFileTransferPart} to be created. + */ + public long getMediaDuration() { + return mMediaDuration; + } + + /** + * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to + * be created. This should only be used for multi-media files. + */ + public Uri getPreviewUri() { + return mPreviewUri; + } + + /** + * @return Returns the MIME type of the preview of the content of the + * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files. + */ + public String getPreviewMimeType() { + return mPreviewMimeType; + } + + /** + * @return Returns the status of the {@link RcsFileTransferPart} to be created. + */ + public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() { + return mFileTransferStatus; + } + + /** + * @hide + */ + RcsFileTransferCreationParams(Builder builder) { + mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId; + mContentUri = builder.mContentUri; + mContentMimeType = builder.mContentMimeType; + mFileSize = builder.mFileSize; + mTransferOffset = builder.mTransferOffset; + mWidth = builder.mWidth; + mHeight = builder.mHeight; + mMediaDuration = builder.mLength; + mPreviewUri = builder.mPreviewUri; + mPreviewMimeType = builder.mPreviewMimeType; + mFileTransferStatus = builder.mFileTransferStatus; + } + + /** + * A builder to create instances of {@link RcsFileTransferCreationParams} + */ + public class Builder { + private String mRcsFileTransferSessionId; + private Uri mContentUri; + private String mContentMimeType; + private long mFileSize; + private long mTransferOffset; + private int mWidth; + private int mHeight; + private long mLength; + private Uri mPreviewUri; + private String mPreviewMimeType; + private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus; + + /** + * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart} + * to be created + * + * @param sessionId The RCS file transfer session ID + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setFileTransferSessionId(String sessionId) { + mRcsFileTransferSessionId = sessionId; + return this; + } + + /** + * Sets the URI for the content of the {@link RcsFileTransferPart} to be created + * + * @param contentUri The URI for the file + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setContentUri(Uri contentUri) { + mContentUri = contentUri; + return this; + } + + /** + * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created + * + * @param contentType The MIME type of the file + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setContentMimeType(String contentType) { + mContentMimeType = contentType; + return this; + } + + /** + * Sets the file size for the {@link RcsFileTransferPart} to be created + * + * @param size The size of the file in bytes + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setFileSize(long size) { + mFileSize = size; + return this; + } + + /** + * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file + * transfer offset is defined as how many bytes have been successfully transferred to the + * receiver of this file transfer. + * + * @param offset The transfer offset in bytes + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setTransferOffset(long offset) { + mTransferOffset = offset; + return this; + } + + /** + * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used + * for multi-media files. + * + * @param width The width of the multi-media file in pixels. + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setWidth(int width) { + mWidth = width; + return this; + } + + /** + * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be + * used for multi-media files. + * + * @param height The height of the multi-media file in pixels. + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setHeight(int height) { + mHeight = height; + return this; + } + + /** + * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be + * used for multi-media files such as audio or video. + * + * @param length The length of the multi-media file in milliseconds + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setMediaDuration(long length) { + mLength = length; + return this; + } + + /** + * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be + * created. This should only be used for multi-media files. + * + * @param previewUri The URI of the preview of the file transfer + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setPreviewUri(Uri previewUri) { + mPreviewUri = previewUri; + return this; + } + + /** + * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to + * be created. This should only be used for multi-media files. + * + * @param previewType The MIME type of the preview of the file transfer + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setPreviewMimeType(String previewType) { + mPreviewMimeType = previewType; + return this; + } + + /** + * Sets the status of the {@link RcsFileTransferPart} to be created. + * + * @param status The status of the file transfer + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setFileTransferStatus( + @RcsFileTransferPart.RcsFileTransferStatus int status) { + mFileTransferStatus = status; + return this; + } + + /** + * Creates an instance of {@link RcsFileTransferCreationParams} with the given + * parameters. + * + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParams) + */ + public RcsFileTransferCreationParams build() { + return new RcsFileTransferCreationParams(this); + } + } + + private RcsFileTransferCreationParams(Parcel in) { + mRcsFileTransferSessionId = in.readString(); + mContentUri = in.readParcelable(Uri.class.getClassLoader()); + mContentMimeType = in.readString(); + mFileSize = in.readLong(); + mTransferOffset = in.readLong(); + mWidth = in.readInt(); + mHeight = in.readInt(); + mMediaDuration = in.readLong(); + mPreviewUri = in.readParcelable(Uri.class.getClassLoader()); + mPreviewMimeType = in.readString(); + mFileTransferStatus = in.readInt(); + } + + public static final Creator<RcsFileTransferCreationParams> CREATOR = + new Creator<RcsFileTransferCreationParams>() { + @Override + public RcsFileTransferCreationParams createFromParcel(Parcel in) { + return new RcsFileTransferCreationParams(in); + } + + @Override + public RcsFileTransferCreationParams[] newArray(int size) { + return new RcsFileTransferCreationParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mRcsFileTransferSessionId); + dest.writeParcelable(mContentUri, flags); + dest.writeString(mContentMimeType); + dest.writeLong(mFileSize); + dest.writeLong(mTransferOffset); + dest.writeInt(mWidth); + dest.writeInt(mHeight); + dest.writeLong(mMediaDuration); + dest.writeParcelable(mPreviewUri, flags); + dest.writeString(mPreviewMimeType); + dest.writeInt(mFileTransferStatus); + } +} diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java index 39c58dd9c15b..1ce799919e09 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java +++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java @@ -15,34 +15,346 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.annotation.WorkerThread; +import android.net.Uri; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** - * A part of a composite {@link RcsMessage} that holds a file transfer. - * @hide - TODO(sahinc) make this public + * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7 + * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide - TODO: make public */ -public class RcsFileTransferPart extends RcsPart { - public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() { - @Override - public RcsFileTransferPart createFromParcel(Parcel in) { - return new RcsFileTransferPart(in); - } +public class RcsFileTransferPart { + /** + * The status to indicate that this {@link RcsFileTransferPart} is not set yet. + */ + public static final int NOT_SET = 0; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the + * process of sending yet. + */ + public static final int DRAFT = 1; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right + * now. + */ + public static final int SENDING = 2; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has + * paused the sending process. + */ + public static final int SENDING_PAUSED = 3; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to + * send. + */ + public static final int SENDING_FAILED = 4; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to + * send. + */ + public static final int SENDING_CANCELLED = 5; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded + * right now. + */ + public static final int DOWNLOADING = 6; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the + * user paused the downloading process. + */ + public static final int DOWNLOADING_PAUSED = 7; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to + * download. + */ + public static final int DOWNLOADING_FAILED = 8; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to + * download. + */ + public static final int DOWNLOADING_CANCELLED = 9; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or + * received. + */ + public static final int SUCCEEDED = 10; + + @IntDef({ + DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING, + DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RcsFileTransferStatus { + } + + private int mId; + + /** + * @hide + */ + RcsFileTransferPart(int id) { + mId = id; + } + + /** + * @hide + */ + public void setId(int id) { + mId = id; + } + + /** + * @hide + */ + public int getId() { + return mId; + } + + /** + * Sets the RCS file transfer session ID for this file transfer and persists into storage. + * + * @param sessionId The session ID to be used for this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferSessionId(mId, sessionId)); + } + + /** + * @return Returns the file transfer session ID. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getFileTransferSessionId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferSessionId(mId)); + } + + /** + * Sets the content URI for this file transfer and persists into storage. The file transfer + * should be reachable using this URI. + * + * @param contentUri The URI for this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setContentUri(Uri contentUri) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferContentUri(mId, contentUri)); + } + + /** + * @return Returns the URI for this file transfer + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public Uri getContentUri() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentUri(mId)); + } - @Override - public RcsFileTransferPart[] newArray(int size) { - return new RcsFileTransferPart[size]; - } - }; + /** + * Sets the MIME type of this file transfer and persists into storage. Whether this type + * actually matches any known or supported types is not checked. + * + * @param contentMimeType The type of this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferContentType(mId, contentMimeType)); + } + + /** + * @return Returns the content type of this file transfer + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + @Nullable + public String getContentMimeType() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentType(mId)); + } + + /** + * Sets the content length (i.e. file size) for this file transfer and persists into storage. + * + * @param contentLength The content length of this file transfer + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFileSize(long contentLength) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferFileSize(mId, contentLength)); + } + + /** + * @return Returns the content length (i.e. file size) for this file transfer. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getFileSize() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferFileSize(mId)); + } + + /** + * Sets the transfer offset for this file transfer and persists into storage. The file transfer + * offset is defined as how many bytes have been successfully transferred to the receiver of + * this file transfer. + * + * @param transferOffset The transfer offset for this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setTransferOffset(long transferOffset) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferTransferOffset(mId, transferOffset)); + } + + /** + * @return Returns the number of bytes that have successfully transferred. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getTransferOffset() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferTransferOffset(mId)); + } + + /** + * Sets the status for this file transfer and persists into storage. + * + * @param status The status of this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFileTransferStatus(@RcsFileTransferStatus int status) + throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferStatus(mId, status)); + } + + /** + * @return Returns the status of this file transfer. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferStatus(mId)); + } + + /** + * @return Returns the width of this multi-media message part in pixels. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public int getWidth() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferWidth(mId)); + } + + /** + * Sets the width of this RCS multi-media message part and persists into storage. + * + * @param width The width value in pixels + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setWidth(int width) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferWidth(mId, width)); + } + + /** + * @return Returns the height of this multi-media message part in pixels. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public int getHeight() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferHeight(mId)); + } + + /** + * Sets the height of this RCS multi-media message part and persists into storage. + * + * @param height The height value in pixels + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setHeight(int height) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferHeight(mId, height)); + } + + /** + * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getLength() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferLength(mId)); + } + + /** + * Sets the length of this multi-media file (e.g. video or audio) and persists into storage. + * + * @param length The length of the file in milliseconds. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setLength(long length) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferLength(mId, length)); + } + + /** + * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for + * a video) + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public Uri getPreviewUri() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewUri(mId)); + } - protected RcsFileTransferPart(Parcel in) { + /** + * Sets the URI for the preview of this multi-media file and persists into storage. + * + * @param previewUri The URI to access to the preview file. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferPreviewUri(mId, previewUri)); } - @Override - public int describeContents() { - return 0; + /** + * @return Returns the MIME type of this multi-media file's preview. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getPreviewMimeType() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewType(mId)); } - @Override - public void writeToParcel(Parcel dest, int flags) { + /** + * Sets the MIME type for this multi-media file's preview and persists into storage. + * + * @param previewMimeType The MIME type for the preview + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferPreviewType(mId, previewMimeType)); } } diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java index d954b2d70ac3..2c42494ee924 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThread.java +++ b/telephony/java/android/telephony/ims/RcsGroupThread.java @@ -15,38 +15,191 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.WorkerThread; +import android.net.Uri; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; /** * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join - * or leave. - * @hide - TODO(sahinc) make this public + * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service + * Definition Document) + * + * @hide - TODO: make public */ public class RcsGroupThread extends RcsThread { - public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() { - @Override - public RcsGroupThread createFromParcel(Parcel in) { - return new RcsGroupThread(in); + /** + * Public constructor only for RcsMessageStoreController to initialize new threads. + * + * @hide + */ + public RcsGroupThread(int threadId) { + super(threadId); + } + + /** + * @return Returns {@code true} as this is always a group thread + */ + @Override + public boolean isGroup() { + return true; + } + + /** + * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71 + * (RCS Universal Profile Service Definition Document) + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public String getGroupName() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadName(mThreadId)); + } + + /** + * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setGroupName(String groupName) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadName(mThreadId, groupName)); + } + + /** + * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see + * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + public Uri getGroupIcon() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadIcon(mThreadId)); + } + + /** + * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadIcon(mThreadId, groupIcon)); + } + + /** + * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public RcsParticipant getOwner() throws RcsMessageStoreException { + return new RcsParticipant(RcsControllerCall.call( + iRcs -> iRcs.getGroupThreadOwner(mThreadId))); + } + + /** + * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to + * be used for selecting a new owner for a group thread if the owner leaves the thread. The + * owner needs to be in the list of existing participants. + * + * @param participant The new owner of the thread. {@code null} values are allowed. + * @throws RcsMessageStoreException if the operation could not be persisted into storage + */ + @WorkerThread + public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setGroupThreadOwner(mThreadId, participant.getId())); + } + + /** + * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user + * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf + * of them should be added. + * + * @param participant The new participant to be added to the thread. + * @throws RcsMessageStoreException if the operation could not be persisted into storage + */ + @WorkerThread + public void addParticipant(@NonNull RcsParticipant participant) + throws RcsMessageStoreException { + if (participant == null) { + return; } - @Override - public RcsGroupThread[] newArray(int size) { - return new RcsGroupThread[size]; + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.addParticipantToGroupThread(mThreadId, participant.getId())); + } + + /** + * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the + * removed participant was the owner of this group, the owner will become null. + * + * @throws RcsMessageStoreException if the operation could not be persisted into storage + */ + @WorkerThread + public void removeParticipant(@NonNull RcsParticipant participant) + throws RcsMessageStoreException { + if (participant == null) { + return; } - }; - protected RcsGroupThread(Parcel in) { - super(in); + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.removeParticipantFromGroupThread(mThreadId, participant.getId())); } - @Override - public int describeContents() { - return 0; + /** + * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The + * returned set does not support modifications, please use + * {@link RcsGroupThread#addParticipant(RcsParticipant)} + * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead. + * + * @return the immutable set of {@link RcsParticipant} in this group thread. + * @throws RcsMessageStoreException if the values could not be read from the storage + */ + @WorkerThread + @NonNull + public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException { + RcsParticipantQueryParams queryParameters = + new RcsParticipantQueryParams.Builder().setThread(this).build(); + + RcsParticipantQueryResult queryResult = RcsControllerCall.call( + iRcs -> iRcs.getParticipants(queryParameters)); + + List<RcsParticipant> participantList = queryResult.getParticipants(); + Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList); + return Collections.unmodifiableSet(participantSet); } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(RCS_GROUP_TYPE); - super.writeToParcel(dest, flags); + /** + * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53 + * (RCS Device API 1.6 Specification + * + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public Uri getConferenceUri() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadConferenceUri(mThreadId)); + } + + /** + * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see + * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification + * + * @param conferenceUri The URI as String to be used as the conference URI. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @Nullable + @WorkerThread + public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri)); } } diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl index 9fdc41d2bd5f..77a23722f080 100644 --- a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl +++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl @@ -1,5 +1,4 @@ /* - * * Copyright 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,4 +16,4 @@ package android.telephony.ims; -parcelable Rcs1To1Thread; +parcelable RcsGroupThreadEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java new file mode 100644 index 000000000000..bc61877a81d6 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; + +/** + * An event that happened on an {@link RcsGroupThread}. + * + * @hide - TODO: make public + */ +public abstract class RcsGroupThreadEvent extends RcsEvent { + private final int mRcsGroupThreadId; + private final int mOriginatingParticipantId; + + RcsGroupThreadEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId) { + super(timestamp); + mRcsGroupThreadId = rcsGroupThreadId; + mOriginatingParticipantId = originatingParticipantId; + } + + /** + * @return Returns the {@link RcsGroupThread} that this event happened on. + */ + @NonNull + public RcsGroupThread getRcsGroupThread() { + return new RcsGroupThread(mRcsGroupThreadId); + } + + /** + * @return Returns the {@link RcsParticipant} that performed the event. + */ + @NonNull + public RcsParticipant getOriginatingParticipant() { + return new RcsParticipant(mOriginatingParticipantId); + } + + /** + * @hide + */ + RcsGroupThreadEvent(Parcel in) { + super(in); + mRcsGroupThreadId = in.readInt(); + mOriginatingParticipantId = in.readInt(); + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mRcsGroupThreadId); + dest.writeInt(mOriginatingParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl new file mode 100644 index 000000000000..daea7922f3df --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl @@ -0,0 +1,19 @@ +/* + * 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. + */ + +package android.telephony.ims; + +parcelable RcsGroupThreadIconChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java new file mode 100644 index 000000000000..74af9738e976 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA + * RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide - TODO: make public + */ +public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent implements + Parcelable { + private final Uri mNewIcon; + + /** + * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that changed the + * {@link RcsGroupThread}'s icon. + * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadIconChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread, + @NonNull RcsParticipant originatingParticipant, @Nullable Uri newIcon) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mNewIcon = newIcon; + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadIconChangedEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, @Nullable Uri newIcon) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mNewIcon = newIcon; + } + + /** + * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this + * {@link RcsGroupThreadIconChangedEvent} occured. + */ + @Nullable + public Uri getNewIcon() { + return mNewIcon; + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + // TODO ensure failure throws + RcsControllerCall.call(iRcs -> iRcs.createGroupThreadIconChangedEvent( + getTimestamp(), getRcsGroupThread().getThreadId(), + getOriginatingParticipant().getId(), mNewIcon)); + } + + public static final Creator<RcsGroupThreadIconChangedEvent> CREATOR = + new Creator<RcsGroupThreadIconChangedEvent>() { + @Override + public RcsGroupThreadIconChangedEvent createFromParcel(Parcel in) { + return new RcsGroupThreadIconChangedEvent(in); + } + + @Override + public RcsGroupThreadIconChangedEvent[] newArray(int size) { + return new RcsGroupThreadIconChangedEvent[size]; + } + }; + + private RcsGroupThreadIconChangedEvent(Parcel in) { + super(in); + mNewIcon = in.readParcelable(Uri.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(mNewIcon, flags); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl new file mode 100644 index 000000000000..3ed9bd11dc70 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsGroupThreadNameChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java new file mode 100644 index 000000000000..06f4d5b10bb4 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA + * RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide - TODO: make public + */ +public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent implements + Parcelable { + private final String mNewName; + + /** + * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that changed the + * {@link RcsGroupThread}'s icon. + * @param newName The new name of the {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread, + @NonNull RcsParticipant originatingParticipant, @Nullable String newName) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mNewName = newName; + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadNameChangedEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, @Nullable String newName) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mNewName = newName; + } + + /** + * @return Returns the name of this {@link RcsGroupThread} after this + * {@link RcsGroupThreadNameChangedEvent} happened. + */ + @Nullable + public String getNewName() { + return mNewName; + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call(iRcs -> iRcs.createGroupThreadNameChangedEvent( + getTimestamp(), getRcsGroupThread().getThreadId(), + getOriginatingParticipant().getId(), mNewName)); + } + + public static final Creator<RcsGroupThreadNameChangedEvent> CREATOR = + new Creator<RcsGroupThreadNameChangedEvent>() { + @Override + public RcsGroupThreadNameChangedEvent createFromParcel(Parcel in) { + return new RcsGroupThreadNameChangedEvent(in); + } + + @Override + public RcsGroupThreadNameChangedEvent[] newArray(int size) { + return new RcsGroupThreadNameChangedEvent[size]; + } + }; + + private RcsGroupThreadNameChangedEvent(Parcel in) { + super(in); + mNewName = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mNewName); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl new file mode 100644 index 000000000000..420abffa067a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl @@ -0,0 +1,20 @@ +/* + * + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable RcsGroupThreadParticipantJoinedEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java new file mode 100644 index 000000000000..493270795e01 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide - TODO: make public + */ +public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent implements + Parcelable { + private final int mJoinedParticipantId; + + /** + * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that added or invited the new + * {@link RcsParticipant} into the {@link RcsGroupThread} + * @param joinedParticipant The new {@link RcsParticipant} that joined the + * {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadParticipantJoinedEvent(long timestamp, + @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant, + @NonNull RcsParticipant joinedParticipant) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mJoinedParticipantId = joinedParticipant.getId(); + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadParticipantJoinedEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, int joinedParticipantId) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mJoinedParticipantId = joinedParticipantId; + } + + /** + * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread} + */ + public RcsParticipant getJoinedParticipant() { + return new RcsParticipant(mJoinedParticipantId); + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call( + iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(), + getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(), + getJoinedParticipant().getId())); + } + + public static final Creator<RcsGroupThreadParticipantJoinedEvent> CREATOR = + new Creator<RcsGroupThreadParticipantJoinedEvent>() { + @Override + public RcsGroupThreadParticipantJoinedEvent createFromParcel(Parcel in) { + return new RcsGroupThreadParticipantJoinedEvent(in); + } + + @Override + public RcsGroupThreadParticipantJoinedEvent[] newArray(int size) { + return new RcsGroupThreadParticipantJoinedEvent[size]; + } + }; + + private RcsGroupThreadParticipantJoinedEvent(Parcel in) { + super(in); + mJoinedParticipantId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mJoinedParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl index b32cd1208c40..ff139ac0ab1e 100644 --- a/telephony/java/android/telephony/ims/RcsMessage.aidl +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsMessage; +parcelable RcsGroupThreadParticipantLeftEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java new file mode 100644 index 000000000000..970a046e1105 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide - TODO: make public + */ +public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent implements + Parcelable { + private final int mLeavingParticipantId; + + /** + * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that removed the + * {@link RcsParticipant} from the {@link RcsGroupThread}. It is + * possible that originatingParticipant and leavingParticipant are + * the same (i.e. {@link RcsParticipant} left the group + * themselves) + * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadParticipantLeftEvent(long timestamp, + @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant, + @NonNull RcsParticipant leavingParticipant) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mLeavingParticipantId = leavingParticipant.getId(); + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadParticipantLeftEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, int leavingParticipantId) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mLeavingParticipantId = leavingParticipantId; + } + + /** + * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread} + * after this {@link RcsGroupThreadParticipantLeftEvent} happened. + */ + @NonNull + public RcsParticipant getLeavingParticipantId() { + return new RcsParticipant(mLeavingParticipantId); + } + + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call( + iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(), + getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(), + getLeavingParticipantId().getId())); + } + + public static final Creator<RcsGroupThreadParticipantLeftEvent> CREATOR = + new Creator<RcsGroupThreadParticipantLeftEvent>() { + @Override + public RcsGroupThreadParticipantLeftEvent createFromParcel(Parcel in) { + return new RcsGroupThreadParticipantLeftEvent(in); + } + + @Override + public RcsGroupThreadParticipantLeftEvent[] newArray(int size) { + return new RcsGroupThreadParticipantLeftEvent[size]; + } + }; + + private RcsGroupThreadParticipantLeftEvent(Parcel in) { + super(in); + mLeavingParticipantId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mLeavingParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java index f39e06db068a..f3b7815c2453 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java +++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java @@ -15,34 +15,82 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.WorkerThread; /** * This is a single instance of a message received over RCS. - * @hide - TODO(sahinc) make this public + * + * @hide - TODO: make public */ public class RcsIncomingMessage extends RcsMessage { - public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() { - @Override - public RcsIncomingMessage createFromParcel(Parcel in) { - return new RcsIncomingMessage(in); - } - - @Override - public RcsIncomingMessage[] newArray(int size) { - return new RcsIncomingMessage[size]; - } - }; - - protected RcsIncomingMessage(Parcel in) { + /** + * @hide + */ + RcsIncomingMessage(int id) { + super(id); } - @Override - public int describeContents() { - return 0; + /** + * Sets the timestamp of arrival for this message and persists into storage. The timestamp is + * defined as milliseconds passed after midnight, January 1, 1970 UTC + * + * @param arrivalTimestamp The timestamp to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageArrivalTimestamp(mId, true, arrivalTimestamp)); + } + + /** + * @return Returns the timestamp of arrival for this message. The timestamp is defined as + * milliseconds passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getArrivalTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageArrivalTimestamp(mId, true)); + } + + /** + * Sets the timestamp of when the user saw this message and persists into storage. The timestamp + * is defined as milliseconds passed after midnight, January 1, 1970 UTC + * + * @param notifiedTimestamp The timestamp to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp)); + } + + /** + * @return Returns the timestamp of when the user saw this message. The timestamp is defined as + * milliseconds passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getSeenTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageSeenTimestamp(mId, true)); + } + + /** + * @return Returns the sender of this incoming message. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public RcsParticipant getSenderParticipant() throws RcsMessageStoreException { + return new RcsParticipant( + RcsControllerCall.call(iRcs -> iRcs.getSenderParticipant(mId))); } + /** + * @return Returns {@code true} as this is an incoming message + */ @Override - public void writeToParcel(Parcel dest, int flags) { + public boolean isIncoming() { + return true; } } diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl new file mode 100644 index 000000000000..1f1d4f68213a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsIncomingMessageCreationParams; diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java new file mode 100644 index 000000000000..64b2339905eb --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java @@ -0,0 +1,180 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed + * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an + * {@link RcsIncomingMessage} on that {@link RcsThread} + * + * @hide - TODO: make public + */ +public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements + Parcelable { + // The arrival timestamp for the RcsIncomingMessage to be created + private final long mArrivalTimestamp; + // The seen timestamp for the RcsIncomingMessage to be created + private final long mSeenTimestamp; + // The participant that sent this incoming message + private final int mSenderParticipantId; + + /** + * Builder to help create an {@link RcsIncomingMessageCreationParams} + * + * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams) + */ + public static class Builder extends RcsMessageCreationParams.Builder { + private RcsParticipant mSenderParticipant; + private long mArrivalTimestamp; + private long mSeenTimestamp; + + /** + * Creates a {@link Builder} to create an instance of + * {@link RcsIncomingMessageCreationParams} + * + * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination + * timestamp value in milliseconds passed after midnight, + * January 1, 1970 UTC + * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after + * midnight, January 1, 1970 UTC + * @param subscriptionId The subscription ID that was used to send or receive this + * {@link RcsMessage} + */ + public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) { + super(originationTimestamp, subscriptionId); + mArrivalTimestamp = arrivalTimestamp; + } + + /** + * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage} + * + * @param senderParticipant The {@link RcsParticipant} that sent this + * {@link RcsIncomingMessage} + * @return The same instance of {@link Builder} to chain methods. + */ + @CheckResult + public Builder setSenderParticipant(RcsParticipant senderParticipant) { + mSenderParticipant = senderParticipant; + return this; + } + + /** + * Sets the time of the arrival of this {@link RcsIncomingMessage} + + * @return The same instance of {@link Builder} to chain methods. + * @see RcsIncomingMessage#setArrivalTimestamp(long) + */ + @CheckResult + public Builder setArrivalTimestamp(long arrivalTimestamp) { + mArrivalTimestamp = arrivalTimestamp; + return this; + } + + /** + * Sets the time of the when this user saw the {@link RcsIncomingMessage} + * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight, + * January 1, 1970 UTC + * @return The same instance of {@link Builder} to chain methods. + * @see RcsIncomingMessage#setSeenTimestamp(long) + */ + @CheckResult + public Builder setSeenTimestamp(long seenTimestamp) { + mSeenTimestamp = seenTimestamp; + return this; + } + + /** + * Creates parameters for creating a new incoming message. + * @return A new instance of {@link RcsIncomingMessageCreationParams} to create a new + * {@link RcsIncomingMessage} + */ + public RcsIncomingMessageCreationParams build() { + return new RcsIncomingMessageCreationParams(this); + } + } + + private RcsIncomingMessageCreationParams(Builder builder) { + super(builder); + mArrivalTimestamp = builder.mArrivalTimestamp; + mSeenTimestamp = builder.mSeenTimestamp; + mSenderParticipantId = builder.mSenderParticipant.getId(); + } + + private RcsIncomingMessageCreationParams(Parcel in) { + super(in); + mArrivalTimestamp = in.readLong(); + mSeenTimestamp = in.readLong(); + mSenderParticipantId = in.readInt(); + } + + /** + * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created. + * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC + */ + public long getArrivalTimestamp() { + return mArrivalTimestamp; + } + + /** + * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created. + * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC + */ + public long getSeenTimestamp() { + return mSeenTimestamp; + } + + /** + * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to + * create {@link RcsIncomingMessage}s + * + * Since the API doesn't expose any ID's to API users, this should be hidden. + * @hide + */ + public int getSenderParticipantId() { + return mSenderParticipantId; + } + + public static final Creator<RcsIncomingMessageCreationParams> CREATOR = + new Creator<RcsIncomingMessageCreationParams>() { + @Override + public RcsIncomingMessageCreationParams createFromParcel(Parcel in) { + return new RcsIncomingMessageCreationParams(in); + } + + @Override + public RcsIncomingMessageCreationParams[] newArray(int size) { + return new RcsIncomingMessageCreationParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest); + dest.writeLong(mArrivalTimestamp); + dest.writeLong(mSeenTimestamp); + dest.writeInt(mSenderParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.aidl b/telephony/java/android/telephony/ims/RcsLocationPart.aidl deleted file mode 100644 index 4fe5ca97a30d..000000000000 --- a/telephony/java/android/telephony/ims/RcsLocationPart.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsLocationPart; diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java deleted file mode 100644 index 19be4ceaf688..000000000000 --- a/telephony/java/android/telephony/ims/RcsLocationPart.java +++ /dev/null @@ -1,48 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * A part of a composite {@link RcsMessage} that holds a location - * @hide - TODO(sahinc) make this public - */ -public class RcsLocationPart extends RcsPart { - public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() { - @Override - public RcsLocationPart createFromParcel(Parcel in) { - return new RcsLocationPart(in); - } - - @Override - public RcsLocationPart[] newArray(int size) { - return new RcsLocationPart[size]; - } - }; - - protected RcsLocationPart(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java index df108c88e3b0..90bd0446d6f3 100644 --- a/telephony/java/android/telephony/ims/RcsManager.java +++ b/telephony/java/android/telephony/ims/RcsManager.java @@ -20,15 +20,23 @@ import android.content.Context; /** * The manager class for RCS related utilities. - * @hide + * + * @hide - TODO: make public */ @SystemService(Context.TELEPHONY_RCS_SERVICE) public class RcsManager { + /** + * @hide + */ + public RcsManager() { + // empty constructor + } + private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore(); /** - * Returns an instance of RcsMessageStore. + * Returns an instance of {@link RcsMessageStore} */ public RcsMessageStore getRcsMessageStore() { return sRcsMessageStoreInstance; diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java index d46685c4a572..1700941b94ed 100644 --- a/telephony/java/android/telephony/ims/RcsMessage.java +++ b/telephony/java/android/telephony/ims/RcsMessage.java @@ -15,11 +15,317 @@ */ package android.telephony.ims; -import android.os.Parcelable; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; /** * This is a single instance of a message sent or received over RCS. - * @hide - TODO(sahinc) make this public + * + * @hide - TODO: make public */ -public abstract class RcsMessage implements Parcelable { +public abstract class RcsMessage { + /** + * The value to indicate that this {@link RcsMessage} does not have any location information. + */ + public static final double LOCATION_NOT_SET = Double.MIN_VALUE; + + /** + * The status to indicate that this {@link RcsMessage}s status is not set yet. + */ + public static final int NOT_SET = 0; + + /** + * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of + * sending yet. + */ + public static final int DRAFT = 1; + + /** + * The status to indicate that this {@link RcsMessage} was successfully sent. + */ + public static final int QUEUED = 2; + + /** + * The status to indicate that this {@link RcsMessage} is actively being sent. + */ + public static final int SENDING = 3; + + /** + * The status to indicate that this {@link RcsMessage} was successfully sent. + */ + public static final int SENT = 4; + + /** + * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and + * now being retried. + */ + public static final int RETRYING = 5; + + /** + * The status to indicate that this {@link RcsMessage} has permanently failed to send. + */ + public static final int FAILED = 6; + + /** + * The status to indicate that this {@link RcsMessage} was successfully received. + */ + public static final int RECEIVED = 7; + + /** + * The status to indicate that this {@link RcsMessage} was seen. + */ + public static final int SEEN = 9; + + /** + * @hide + */ + protected final int mId; + + @IntDef({ + DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RcsMessageStatus { + } + + RcsMessage(int id) { + mId = id; + } + + /** + * Returns the row Id from the common message. + * + * @hide + */ + public int getId() { + return mId; + } + + /** + * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered + * to. + * @throws RcsMessageStoreException if the value could not be read from the storage + * @see android.telephony.SubscriptionInfo#getSubscriptionId + */ + public int getSubscriptionId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageSubId(mId, isIncoming())); + } + + /** + * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and + * persists it into storage. + * + * @param subId The subscription ID to persists into storage. + * @throws RcsMessageStoreException if the value could not be persisted into storage + * @see android.telephony.SubscriptionInfo#getSubscriptionId + */ + @WorkerThread + public void setSubscriptionId(int subId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setMessageSubId(mId, isIncoming(), subId)); + } + + /** + * Sets the status of this message and persists it into storage. Please see + * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers. + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus)); + } + + /** + * @return Returns the status of this message. Please see + * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public @RcsMessageStatus int getStatus() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageStatus(mId, isIncoming())); + } + + /** + * Sets the origination timestamp of this message and persists it into storage. Origination is + * defined as when the sender tapped the send button. + * + * @param timestamp The origination timestamp value in milliseconds passed after midnight, + * January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(), timestamp)); + } + + /** + * @return Returns the origination timestamp of this message in milliseconds passed after + * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send + * button. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getOriginationTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call( + iRcs -> iRcs.getMessageOriginationTimestamp(mId, isIncoming())); + } + + /** + * Sets the globally unique RCS message identifier for this message and persists it into + * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2 + * - GSMA RCC.53 (RCS Device API 1.6 Specification + * + * @param rcsMessageGlobalId The globally RCS message identifier + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(), rcsMessageGlobalId)); + } + + /** + * @return Returns the globally unique RCS message identifier for this message. Please see + * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getRcsMessageId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming())); + } + + /** + * @return Returns the user visible text included in this message. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getText() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getTextForMessage(mId, isIncoming())); + } + + /** + * Sets the user visible text for this message and persists in storage. + * + * @param text The text this message now has + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setText(String text) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setTextForMessage(mId, isIncoming(), text)); + } + + /** + * @return Returns the associated latitude for this message, or + * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location. + * + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public double getLatitude() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getLatitudeForMessage(mId, isIncoming())); + } + + /** + * Sets the latitude for this message and persists in storage. + * + * @param latitude The latitude for this location message. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setLatitude(double latitude) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude)); + } + + /** + * @return Returns the associated longitude for this message, or + * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location. + * + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public double getLongitude() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getLongitudeForMessage(mId, isIncoming())); + } + + /** + * Sets the longitude for this message and persists in storage. + * + * @param longitude The longitude for this location message. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setLongitude(double longitude) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude)); + } + + /** + * Attaches an {@link RcsFileTransferPart} to this message and persists into storage. + * + * @param fileTransferCreationParameters The parameters to be used to create the + * {@link RcsFileTransferPart} + * @return A new instance of {@link RcsFileTransferPart} + * @throws RcsMessageStoreException if the file transfer could not be persisted into storage. + */ + @NonNull + @WorkerThread + public RcsFileTransferPart insertFileTransfer( + RcsFileTransferCreationParams fileTransferCreationParameters) + throws RcsMessageStoreException { + return new RcsFileTransferPart(RcsControllerCall.call( + iRcs -> iRcs.storeFileTransfer(mId, isIncoming(), fileTransferCreationParameters))); + } + + /** + * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an + * unmodifiable set. + * @throws RcsMessageStoreException if the file transfers could not be read from the storage + */ + @NonNull + @WorkerThread + public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException { + Set<RcsFileTransferPart> fileTransferParts = new HashSet<>(); + + int[] fileTransferIds = RcsControllerCall.call( + iRcs -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming())); + + for (int fileTransfer : fileTransferIds) { + fileTransferParts.add(new RcsFileTransferPart(fileTransfer)); + } + + return Collections.unmodifiableSet(fileTransferParts); + } + + /** + * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage. + * + * @param fileTransferPart The part to delete. + * @throws RcsMessageStoreException if the file transfer could not be removed from storage + */ + @WorkerThread + public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart) + throws RcsMessageStoreException { + if (fileTransferPart == null) { + return; + } + + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.deleteFileTransfer(fileTransferPart.getId())); + } + + /** + * @return Returns {@code true} if this message was received on this device, {@code false} if it + * was sent. + */ + public abstract boolean isIncoming(); } diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java new file mode 100644 index 000000000000..9ac4dcdf2d74 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java @@ -0,0 +1,242 @@ +/* + * 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.telephony.ims; + +import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET; + +import android.annotation.CheckResult; +import android.annotation.Nullable; +import android.os.Parcel; + +/** + * The collection of parameters to be passed into + * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and + * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist + * {@link RcsMessage}s on an {@link RcsThread} + * + * @hide - TODO: make public + */ +public class RcsMessageCreationParams { + // The globally unique id of the RcsMessage to be created. + private final String mRcsMessageGlobalId; + + // The subscription that this message was/will be received/sent from. + private final int mSubId; + // The sending/receiving status of the message + private final @RcsMessage.RcsMessageStatus int mMessageStatus; + // The timestamp of message creation + private final long mOriginationTimestamp; + // The user visible content of the message + private final String mText; + // The latitude of the message if this is a location message + private final double mLatitude; + // The longitude of the message if this is a location message + private final double mLongitude; + + /** + * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created. + * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification + */ + @Nullable + public String getRcsMessageGlobalId() { + return mRcsMessageGlobalId; + } + + /** + * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage} + * to be created. + */ + public int getSubId() { + return mSubId; + } + + /** + * @return Returns the status for the {@link RcsMessage} to be created. + * @see RcsMessage.RcsMessageStatus + */ + public int getMessageStatus() { + return mMessageStatus; + } + + /** + * @return Returns the origination timestamp of the {@link RcsMessage} to be created in + * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when + * the sender tapped the send button. + */ + public long getOriginationTimestamp() { + return mOriginationTimestamp; + } + + /** + * @return Returns the user visible text contained in the {@link RcsMessage} to be created + */ + @Nullable + public String getText() { + return mText; + } + + /** + * @return Returns the latitude of the {@link RcsMessage} to be created, or + * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location. + */ + public double getLatitude() { + return mLatitude; + } + + /** + * @return Returns the longitude of the {@link RcsMessage} to be created, or + * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location. + */ + public double getLongitude() { + return mLongitude; + } + + /** + * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s. + * + * @see RcsIncomingMessageCreationParams + */ + public static class Builder { + private String mRcsMessageGlobalId; + private int mSubId; + private @RcsMessage.RcsMessageStatus int mMessageStatus; + private long mOriginationTimestamp; + private String mText; + private double mLatitude = LOCATION_NOT_SET; + private double mLongitude = LOCATION_NOT_SET; + + /** + * @hide + */ + public Builder(long originationTimestamp, int subscriptionId) { + mOriginationTimestamp = originationTimestamp; + mSubId = subscriptionId; + } + + /** + * Sets the status of the {@link RcsMessage} to be built. + * + * @param rcsMessageStatus The status to be set + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setStatus(int) + */ + @CheckResult + public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) { + mMessageStatus = rcsMessageStatus; + return this; + } + + /** + * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built. + * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA + * RCC.53 (RCS Device API 1.6 Specification) + * + * @param rcsMessageId The ID to be set + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setRcsMessageId(String) + */ + @CheckResult + public Builder setRcsMessageId(String rcsMessageId) { + mRcsMessageGlobalId = rcsMessageId; + return this; + } + + /** + * Sets the text of the {@link RcsMessage} to be built. + * + * @param text The user visible text of the message + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setText(String) + */ + @CheckResult + public Builder setText(String text) { + mText = text; + return this; + } + + /** + * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71 + * (RCS Universal Profile Service Definition Document) + * + * @param latitude The latitude of the location information associated with this message. + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setLatitude(double) + */ + @CheckResult + public Builder setLatitude(double latitude) { + mLatitude = latitude; + return this; + } + + /** + * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71 + * (RCS Universal Profile Service Definition Document) + * + * @param longitude The longitude of the location information associated with this message. + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setLongitude(double) + */ + @CheckResult + public Builder setLongitude(double longitude) { + mLongitude = longitude; + return this; + } + + /** + * @return Builds and returns a newly created {@link RcsMessageCreationParams} + */ + public RcsMessageCreationParams build() { + return new RcsMessageCreationParams(this); + } + } + + protected RcsMessageCreationParams(Builder builder) { + mRcsMessageGlobalId = builder.mRcsMessageGlobalId; + mSubId = builder.mSubId; + mMessageStatus = builder.mMessageStatus; + mOriginationTimestamp = builder.mOriginationTimestamp; + mText = builder.mText; + mLatitude = builder.mLatitude; + mLongitude = builder.mLongitude; + } + + /** + * @hide + */ + RcsMessageCreationParams(Parcel in) { + mRcsMessageGlobalId = in.readString(); + mSubId = in.readInt(); + mMessageStatus = in.readInt(); + mOriginationTimestamp = in.readLong(); + mText = in.readString(); + mLatitude = in.readDouble(); + mLongitude = in.readDouble(); + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest) { + dest.writeString(mRcsMessageGlobalId); + dest.writeInt(mSubId); + dest.writeInt(mMessageStatus); + dest.writeLong(mOriginationTimestamp); + dest.writeString(mText); + dest.writeDouble(mLatitude); + dest.writeDouble(mLongitude); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl new file mode 100644 index 000000000000..e9cbd9cc4ebe --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsMessageQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java new file mode 100644 index 000000000000..fae0d97cd722 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java @@ -0,0 +1,361 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; + +/** + * The parameters to pass into + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a + * subset of {@link RcsMessage}s present in the message store. + * + * @hide - TODO: make public + */ +public final class RcsMessageQueryParams implements Parcelable { + /** + * @hide - not meant for public use + */ + public static final int THREAD_ID_NOT_SET = -1; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster + * results. + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()} + */ + public static final int SORT_BY_TIMESTAMP = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP}) + public @interface SortingProperty { + } + + /** + * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return + * {@link RcsIncomingMessage}s. + */ + public static final int MESSAGE_TYPE_INCOMING = 0x0001; + + /** + * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return + * {@link RcsOutgoingMessage}s. + */ + public static final int MESSAGE_TYPE_OUTGOING = 0x0002; + + /** + * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s + * that have an {@link RcsFileTransferPart} attached. + */ + public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004; + + /** + * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s + * that don't have an {@link RcsFileTransferPart} attached. + */ + public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008; + + /** + * @hide - not meant for public use + */ + public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters"; + + // Whether the result should be filtered against incoming or outgoing messages + private int mMessageType; + // Whether the result should have file transfer messages attached or not + private int mFileTransferPresence; + // The SQL "Like" clause to filter messages + private String mMessageLike; + // The property the messages should be sorted against + private @SortingProperty int mSortingProperty; + // Whether the messages should be sorted in ascending order + private boolean mIsAscending; + // The number of results that should be returned with this query + private int mLimit; + // The thread that the results should be limited to + private int mThreadId; + + RcsMessageQueryParams(int messageType, int fileTransferPresence, String messageLike, + int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) { + mMessageType = messageType; + mFileTransferPresence = fileTransferPresence; + mMessageLike = messageLike; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + mLimit = limit; + mThreadId = threadId; + } + + /** + * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParams} + * is set to query for. + */ + public int getMessageType() { + return mMessageType; + } + + /** + * @return Returns whether the result query should return {@link RcsMessage}s with + * {@link RcsFileTransferPart}s or not + */ + public int getFileTransferPresence() { + return mFileTransferPresence; + } + + /** + * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s + */ + public String getMessageLike() { + return mMessageLike; + } + + /** + * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of + * 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property that will be used to sort the result against. + * @see SortingProperty + */ + public @SortingProperty int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get + * the thread that the result query should be limited to. + * + * As we do not expose any sort of integer ID's to public usage, this should be hidden. + * + * @hide - not meant for public use + */ + public int getThreadId() { + return mThreadId; + } + + /** + * A helper class to build the {@link RcsMessageQueryParams}. + */ + public static class Builder { + private @SortingProperty int mSortingProperty; + private int mMessageType; + private int mFileTransferPresence; + private String mMessageLike; + private boolean mIsAscending; + private int mLimit = 100; + private int mThreadId = THREAD_ID_NOT_SET; + + /** + * Creates a new builder for {@link RcsMessageQueryParams} to be used in + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * + */ + public Builder() { + // empty implementation + } + + /** + * Desired number of threads to be returned from the query. Passing in 0 will return all + * existing threads at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the type of messages to be returned from the query. + * + * @param messageType The type of message to be returned. + * @return The same instance of the builder to chain parameters. + * @see RcsMessageQueryParams#MESSAGE_TYPE_INCOMING + * @see RcsMessageQueryParams#MESSAGE_TYPE_OUTGOING + */ + @CheckResult + public Builder setMessageType(int messageType) { + mMessageType = messageType; + return this; + } + + /** + * Sets whether file transfer messages should be included in the query result or not. + * + * @param fileTransferPresence Whether file transfers should be included in the result + * @return The same instance of the builder to chain parameters. + * @see RcsMessageQueryParams#MESSAGES_WITH_FILE_TRANSFERS + * @see RcsMessageQueryParams#MESSAGES_WITHOUT_FILE_TRANSFERS + */ + @CheckResult + public Builder setFileTransferPresence(int fileTransferPresence) { + mFileTransferPresence = fileTransferPresence; + return this; + } + + /** + * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%') + * wildcard matches any sequence of zero or more characters. Using an underscore ('_') + * wildcard matches any single character. Not using any wildcards would only perform a + * string match. The input string is case-insensitive. + * + * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_" + * would only match "who" + * + * @param messageLike The "like" clause for matching {@link RcsMessage}s. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setMessageLike(String messageLike) { + mMessageLike = messageLike; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link RcsMessageQueryParams.SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty against which property the results should be sorted + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Limits the results to the given thread. + * + * @param thread the {@link RcsThread} that results should be limited to. If set to + * {@code null}, messages on all threads will be queried + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setThread(@Nullable RcsThread thread) { + if (thread == null) { + mThreadId = THREAD_ID_NOT_SET; + } else { + mThreadId = thread.getThreadId(); + } + return this; + } + + /** + * Builds the {@link RcsMessageQueryParams} to use in + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * + * @return An instance of {@link RcsMessageQueryParams} to use with the message + * query. + */ + public RcsMessageQueryParams build() { + return new RcsMessageQueryParams(mMessageType, mFileTransferPresence, mMessageLike, + mThreadId, mSortingProperty, mIsAscending, mLimit); + } + } + + /** + * Parcelable boilerplate below. + */ + private RcsMessageQueryParams(Parcel in) { + mMessageType = in.readInt(); + mFileTransferPresence = in.readInt(); + mMessageLike = in.readString(); + mSortingProperty = in.readInt(); + mIsAscending = in.readBoolean(); + mLimit = in.readInt(); + mThreadId = in.readInt(); + } + + public static final Creator<RcsMessageQueryParams> CREATOR = + new Creator<RcsMessageQueryParams>() { + @Override + public RcsMessageQueryParams createFromParcel(Parcel in) { + return new RcsMessageQueryParams(in); + } + + @Override + public RcsMessageQueryParams[] newArray(int size) { + return new RcsMessageQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mMessageType); + dest.writeInt(mFileTransferPresence); + dest.writeString(mMessageLike); + dest.writeInt(mSortingProperty); + dest.writeBoolean(mIsAscending); + dest.writeInt(mLimit); + dest.writeInt(mThreadId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl new file mode 100644 index 000000000000..a73ba50b6591 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsMessageQueryResult; diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java new file mode 100644 index 000000000000..5adab760d594 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java @@ -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. + */ + +package android.telephony.ims; + +import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.ims.RcsTypeIdPair; + +import java.util.ArrayList; +import java.util.List; + +/** + * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * call. This class allows getting the token for querying the next batch of messages in order to + * prevent handling large amounts of data at once. + * + * @hide - TODO: make public + */ +public final class RcsMessageQueryResult implements Parcelable { + // The token to continue the query to get the next batch of results + private RcsQueryContinuationToken mContinuationToken; + // The message type and message ID pairs for all the messages in this query result + private List<RcsTypeIdPair> mMessageTypeIdPairs; + + /** + * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create query results + * + * @hide + */ + public RcsMessageQueryResult( + RcsQueryContinuationToken continuationToken, + List<RcsTypeIdPair> messageTypeIdPairs) { + mContinuationToken = continuationToken; + mMessageTypeIdPairs = messageTypeIdPairs; + } + + /** + * Returns a token to call + * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} + * to get the next batch of {@link RcsMessage}s. + */ + @Nullable + public RcsQueryContinuationToken getContinuationToken() { + return mContinuationToken; + } + + /** + * Returns all the {@link RcsMessage}s in the current query result. Call {@link + * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch + * of {@link RcsMessage}s. + */ + @NonNull + public List<RcsMessage> getMessages() { + List<RcsMessage> messages = new ArrayList<>(); + for (RcsTypeIdPair typeIdPair : mMessageTypeIdPairs) { + if (typeIdPair.getType() == MESSAGE_TYPE_INCOMING) { + messages.add(new RcsIncomingMessage(typeIdPair.getId())); + } else { + messages.add(new RcsOutgoingMessage(typeIdPair.getId())); + } + } + + return messages; + } + + private RcsMessageQueryResult(Parcel in) { + mContinuationToken = in.readParcelable( + RcsQueryContinuationToken.class.getClassLoader()); + in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR); + } + + public static final Creator<RcsMessageQueryResult> CREATOR = + new Creator<RcsMessageQueryResult>() { + @Override + public RcsMessageQueryResult createFromParcel(Parcel in) { + return new RcsMessageQueryResult(in); + } + + @Override + public RcsMessageQueryResult[] newArray(int size) { + return new RcsMessageQueryResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mContinuationToken, flags); + dest.writeTypedList(mMessageTypeIdPairs); + } +} diff --git a/telephony/java/android/telephony/ims/RcsManager.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl index 63bc71c5ee46..99b8eb704e00 100644 --- a/telephony/java/android/telephony/ims/RcsManager.aidl +++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsManager; +parcelable RcsMessageSnippet; diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java new file mode 100644 index 000000000000..565bb99de89a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java @@ -0,0 +1,98 @@ +/* + * 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.telephony.ims; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.RcsMessage.RcsMessageStatus; + +/** + * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread} + * + * @hide - TODO: make public + */ +public final class RcsMessageSnippet implements Parcelable { + private final String mText; + private final @RcsMessageStatus int mStatus; + private final long mTimestamp; + + /** + * @hide + */ + public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) { + mText = text; + mStatus = status; + mTimestamp = timestamp; + } + + /** + * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value + * (i.e. latest) in this thread + */ + @Nullable + public String getSnippetText() { + return mText; + } + + /** + * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value + * (i.e. latest) in this thread + */ + public @RcsMessageStatus int getSnippetStatus() { + return mStatus; + } + + /** + * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp + * value (i.e. latest) in this thread + */ + public long getSnippetTimestamp() { + return mTimestamp; + } + + private RcsMessageSnippet(Parcel in) { + mText = in.readString(); + mStatus = in.readInt(); + mTimestamp = in.readLong(); + } + + public static final Creator<RcsMessageSnippet> CREATOR = + new Creator<RcsMessageSnippet>() { + @Override + public RcsMessageSnippet createFromParcel(Parcel in) { + return new RcsMessageSnippet(in); + } + + @Override + public RcsMessageSnippet[] newArray(int size) { + return new RcsMessageSnippet[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mText); + dest.writeInt(mStatus); + dest.writeLong(mTimestamp); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java index 1bf6ffd81ca0..eca5ed518521 100644 --- a/telephony/java/android/telephony/ims/RcsMessageStore.java +++ b/telephony/java/android/telephony/ims/RcsMessageStore.java @@ -16,106 +16,223 @@ package android.telephony.ims; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.telephony.Rlog; -import android.telephony.ims.aidl.IRcs; +import android.net.Uri; + +import java.util.List; /** * RcsMessageStore is the application interface to RcsProvider and provides access methods to * RCS related database tables. - * @hide - TODO make this public + * + * @hide - TODO: make public */ public class RcsMessageStore { - static final String TAG = "RcsMessageStore"; - /** * Returns the first chunk of existing {@link RcsThread}s in the common storage. + * * @param queryParameters Parameters to specify to return a subset of all RcsThreads. * Passing a value of null will return all threads. + * @throws RcsMessageStoreException if the query could not be completed on the storage */ @WorkerThread - public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.getRcsThreads(queryParameters); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re); - } - - return null; + @NonNull + public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParams queryParameters) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsThreads(queryParameters)); } /** * Returns the next chunk of {@link RcsThread}s in the common storage. + * * @param continuationToken A token to continue the query to get the next chunk. This is - * obtained through {@link RcsThreadQueryResult#nextChunkToken}. + * obtained through {@link RcsThreadQueryResult#getContinuationToken}. + * @throws RcsMessageStoreException if the query could not be completed on the storage */ @WorkerThread - public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.getRcsThreadsWithToken(continuationToken); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re); - } + @NonNull + public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsThreadsWithToken(continuationToken)); + } + + /** + * Returns the first chunk of existing {@link RcsParticipant}s in the common storage. + * + * @param queryParameters Parameters to specify to return a subset of all RcsParticipants. + * Passing a value of null will return all participants. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsParticipantQueryResult getRcsParticipants( + @Nullable RcsParticipantQueryParams queryParameters) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getParticipants(queryParameters)); + } + + /** + * Returns the next chunk of {@link RcsParticipant}s in the common storage. + * + * @param continuationToken A token to continue the query to get the next chunk. This is + * obtained through + * {@link RcsParticipantQueryResult#getContinuationToken} + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsParticipantQueryResult getRcsParticipants( + @NonNull RcsQueryContinuationToken continuationToken) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getParticipantsWithToken(continuationToken)); + } - return null; + /** + * Returns the first chunk of existing {@link RcsMessage}s in the common storage. + * + * @param queryParameters Parameters to specify to return a subset of all RcsMessages. + * Passing a value of null will return all messages. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsMessageQueryResult getRcsMessages( + @Nullable RcsMessageQueryParams queryParameters) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters)); + } + + /** + * Returns the next chunk of {@link RcsMessage}s in the common storage. + * + * @param continuationToken A token to continue the query to get the next chunk. This is + * obtained through {@link RcsMessageQueryResult#getContinuationToken} + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsMessageQueryResult getRcsMessages( + @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessagesWithToken(continuationToken)); + } + + /** + * Returns the first chunk of existing {@link RcsEvent}s in the common storage. + * + * @param queryParameters Parameters to specify to return a subset of all RcsEvents. + * Passing a value of null will return all events. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsEventQueryResult getRcsEvents( + @Nullable RcsEventQueryParams queryParameters) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getEvents(queryParameters)); + } + + /** + * Returns the next chunk of {@link RcsEvent}s in the common storage. + * + * @param continuationToken A token to continue the query to get the next chunk. This is + * obtained through {@link RcsEventQueryResult#getContinuationToken}. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsEventQueryResult getRcsEvents( + @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getEventsWithToken(continuationToken)); + } + + /** + * Persists an {@link RcsEvent} to common storage. + * + * @param persistableEvent The {@link RcsEvent} to persist into storage. + * @throws RcsMessageStoreException if the query could not be completed on the storage + * + * @see RcsGroupThreadNameChangedEvent + * @see RcsGroupThreadIconChangedEvent + * @see RcsGroupThreadParticipantJoinedEvent + * @see RcsGroupThreadParticipantLeftEvent + * @see RcsParticipantAliasChangedEvent + */ + @WorkerThread + @NonNull + public void persistRcsEvent(RcsEvent persistableEvent) throws RcsMessageStoreException { + persistableEvent.persist(); } /** * Creates a new 1 to 1 thread with the given participant and persists it in the storage. + * + * @param recipient The {@link RcsParticipant} that will receive the messages in this thread. + * @return The newly created {@link Rcs1To1Thread} + * @throws RcsMessageStoreException if the thread could not be persisted in the storage */ @WorkerThread - public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.createRcs1To1Thread(recipient); + @NonNull + public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient) + throws RcsMessageStoreException { + return new Rcs1To1Thread( + RcsControllerCall.call(iRcs -> iRcs.createRcs1To1Thread(recipient.getId()))); + } + + /** + * Creates a new group thread with the given participants and persists it in the storage. + * + * @throws RcsMessageStoreException if the thread could not be persisted in the storage + */ + @WorkerThread + @NonNull + public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients, + @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException { + int[] recipientIds = null; + if (recipients != null) { + recipientIds = new int[recipients.size()]; + + for (int i = 0; i < recipients.size(); i++) { + recipientIds[i] = recipients.get(i).getId(); } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re); } - return null; + int[] finalRecipientIds = recipientIds; + return new RcsGroupThread(RcsControllerCall.call( + iRcs -> iRcs.createGroupThread(finalRecipientIds, groupName, groupIcon))); } /** - * Delete the {@link RcsThread} identified by the given threadId. - * @param threadId threadId of the thread to be deleted. + * Delete the given {@link RcsThread} from the storage. + * + * @param thread The thread to be deleted. + * @throws RcsMessageStoreException if the thread could not be deleted from the storage */ @WorkerThread - public void deleteThread(int threadId) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - iRcs.deleteThread(threadId); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re); + public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException { + if (thread == null) { + return; + } + + boolean isDeleteSucceeded = RcsControllerCall.call( + iRcs -> iRcs.deleteThread(thread.getThreadId(), thread.getThreadType())); + + if (!isDeleteSucceeded) { + throw new RcsMessageStoreException("Could not delete RcsThread"); } } /** * Creates a new participant and persists it in the storage. + * * @param canonicalAddress The defining address (e.g. phone number) of the participant. + * @param alias The RCS alias for the participant. + * @throws RcsMessageStoreException if the participant could not be created on the storage */ - public RcsParticipant createRcsParticipant(String canonicalAddress) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.createRcsParticipant(canonicalAddress); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re); - } - - return null; + @WorkerThread + @NonNull + public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias) + throws RcsMessageStoreException { + return new RcsParticipant( + RcsControllerCall.call(iRcs -> iRcs.createRcsParticipant(canonicalAddress, alias))); } } diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java new file mode 100644 index 000000000000..7c00749b1690 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java @@ -0,0 +1,35 @@ +/* + * 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.telephony.ims; + +/** + * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in + * {@link android.telephony.ims} + * + * @hide - TODO: make public + */ +public class RcsMessageStoreException extends Exception { + + /** + * Constructs an {@link RcsMessageStoreException} with the specified detail message. + * @param message The detail message + * @see Throwable#getMessage() + */ + public RcsMessageStoreException(String message) { + super(message); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java deleted file mode 100644 index d295fba365f0..000000000000 --- a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java +++ /dev/null @@ -1,50 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen - * (i.e. image, video etc) - * @hide - TODO(sahinc) make this public - */ -public class RcsMultiMediaPart extends RcsFileTransferPart { - public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() { - @Override - public RcsMultiMediaPart createFromParcel(Parcel in) { - return new RcsMultiMediaPart(in); - } - - @Override - public RcsMultiMediaPart[] newArray(int size) { - return new RcsMultiMediaPart[size]; - } - }; - - protected RcsMultiMediaPart(Parcel in) { - super(in); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl deleted file mode 100644 index 5992d95c3b9c..000000000000 --- a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsMultimediaPart; diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl deleted file mode 100644 index 6e0c80f3af81..000000000000 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsOutgoingMessage; diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java index bfb161133618..dfcdee4a803d 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java @@ -15,34 +15,53 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import java.util.ArrayList; +import java.util.List; /** * This is a single instance of a message sent over RCS. - * @hide - TODO(sahinc) make this public + * + * @hide - TODO: make public */ public class RcsOutgoingMessage extends RcsMessage { - public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() { - @Override - public RcsOutgoingMessage createFromParcel(Parcel in) { - return new RcsOutgoingMessage(in); - } + RcsOutgoingMessage(int id) { + super(id); + } - @Override - public RcsOutgoingMessage[] newArray(int size) { - return new RcsOutgoingMessage[size]; - } - }; + /** + * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please + * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the + * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have + * changed. + * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage. + */ + @NonNull + @WorkerThread + public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries() + throws RcsMessageStoreException { + int[] deliveryParticipants; + List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>(); - protected RcsOutgoingMessage(Parcel in) { - } + deliveryParticipants = RcsControllerCall.call( + iRcs -> iRcs.getMessageRecipients(mId)); - @Override - public int describeContents() { - return 0; + if (deliveryParticipants != null) { + for (Integer deliveryParticipant : deliveryParticipants) { + messageDeliveries.add(new RcsOutgoingMessageDelivery(deliveryParticipant, mId)); + } + } + + return messageDeliveries; } + /** + * @return Returns {@code false} as this is not an incoming message. + */ @Override - public void writeToParcel(Parcel dest, int flags) { + public boolean isIncoming() { + return false; } } diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl new file mode 100644 index 000000000000..0c38d9f5766b --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsOutgoingMessageCreationParams; diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java new file mode 100644 index 000000000000..ca466e8c9d3e --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java @@ -0,0 +1,89 @@ +/* + * 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.telephony.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed + * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an + * {@link RcsOutgoingMessage} on that {@link RcsThread} + * + * @hide - TODO: make public + */ +public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams + implements Parcelable { + /** + * A builder to instantiate and persist an {@link RcsOutgoingMessage} + */ + public static class Builder extends RcsMessageCreationParams.Builder { + + /** + * Creates a new {@link Builder} to create an instance of + * {@link RcsOutgoingMessageCreationParams}. + * + * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination + * timestamp value in milliseconds passed after midnight, + * January 1, 1970 UTC + * @param subscriptionId The subscription ID that was used to send or receive this + * {@link RcsMessage} + * @see android.telephony.SubscriptionInfo#getSubscriptionId() + */ + public Builder(long originationTimestamp, int subscriptionId) { + super(originationTimestamp, subscriptionId); + } + + /** + * Creates configuration parameters for a new message. + */ + public RcsOutgoingMessageCreationParams build() { + return new RcsOutgoingMessageCreationParams(this); + } + } + + private RcsOutgoingMessageCreationParams(Builder builder) { + super(builder); + } + + private RcsOutgoingMessageCreationParams(Parcel in) { + super(in); + } + + public static final Creator<RcsOutgoingMessageCreationParams> CREATOR = + new Creator<RcsOutgoingMessageCreationParams>() { + @Override + public RcsOutgoingMessageCreationParams createFromParcel(Parcel in) { + return new RcsOutgoingMessageCreationParams(in); + } + + @Override + public RcsOutgoingMessageCreationParams[] newArray(int size) { + return new RcsOutgoingMessageCreationParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest); + } +} diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java new file mode 100644 index 000000000000..5a3062a69e3f --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +/** + * This class holds the delivery information of an {@link RcsOutgoingMessage} for each + * {@link RcsParticipant} that the message was intended for. + * + * @hide - TODO: make public + */ +public class RcsOutgoingMessageDelivery { + // The participant that this delivery is intended for + private final int mRecipientId; + // The message this delivery is associated with + private final int mRcsOutgoingMessageId; + + /** + * Constructor to be used with RcsOutgoingMessage.getDelivery() + * + * @hide + */ + RcsOutgoingMessageDelivery(int recipientId, int messageId) { + mRecipientId = recipientId; + mRcsOutgoingMessageId = messageId; + } + + /** + * Sets the delivery time of this outgoing delivery and persists into storage. + * + * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds + * passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryDeliveredTimestamp( + mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp)); + } + + /** + * @return Returns the delivered timestamp of the associated message to the associated + * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC. + * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getDeliveredTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getOutgoingDeliveryDeliveredTimestamp( + mRcsOutgoingMessageId, mRecipientId)); + } + + /** + * Sets the seen time of this outgoing delivery and persists into storage. + * + * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds + * passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliverySeenTimestamp( + mRcsOutgoingMessageId, mRecipientId, seenTimestamp)); + } + + /** + * @return Returns the seen timestamp of the associated message by the associated + * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC. + * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getSeenTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call( + iRcs -> iRcs.getOutgoingDeliverySeenTimestamp(mRcsOutgoingMessageId, mRecipientId)); + } + + /** + * Sets the status of this outgoing delivery and persists into storage. + * + * @param status The status of the associated {@link RcsMessage}s delivery to the associated + * {@link RcsParticipant} + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryStatus( + mRcsOutgoingMessageId, mRecipientId, status)); + } + + /** + * @return Returns the status of this outgoing delivery. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException { + return RcsControllerCall.call( + iRcs -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId, mRecipientId)); + } + + /** + * @return Returns the recipient associated with this delivery. + */ + @NonNull + public RcsParticipant getRecipient() { + return new RcsParticipant(mRecipientId); + } + + /** + * @return Returns the {@link RcsOutgoingMessage} associated with this delivery. + */ + @NonNull + public RcsOutgoingMessage getMessage() { + return new RcsOutgoingMessage(mRcsOutgoingMessageId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsParticipant.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl deleted file mode 100644 index 1c4436367e54..000000000000 --- a/telephony/java/android/telephony/ims/RcsParticipant.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsParticipant; diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java index f678ec7e435b..37b827b8e83c 100644 --- a/telephony/java/android/telephony/ims/RcsParticipant.java +++ b/telephony/java/android/telephony/ims/RcsParticipant.java @@ -15,33 +15,17 @@ */ package android.telephony.ims; -import static android.telephony.ims.RcsMessageStore.TAG; - -import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.WorkerThread; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.telephony.Rlog; -import android.telephony.ims.aidl.IRcs; -import android.text.TextUtils; - -import com.android.internal.util.Preconditions; /** * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s. - * @hide - TODO(sahinc) make this public + * + * @hide - TODO: make public */ -public class RcsParticipant implements Parcelable { +public class RcsParticipant { // The row ID of this participant in the database private int mId; - // The phone number of this participant - private String mCanonicalAddress; - // The RCS alias of this participant. This is different than the name of the contact in the - // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't - // require other users to add them as contacts and give them a name. - private String mAlias; /** * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} @@ -49,106 +33,95 @@ public class RcsParticipant implements Parcelable { * * @hide */ - public RcsParticipant(int id, @NonNull String canonicalAddress) { + public RcsParticipant(int id) { mId = id; - mCanonicalAddress = canonicalAddress; } /** - * @return Returns the canonical address (i.e. normalized phone number) for this participant + * @return Returns the canonical address (i.e. normalized phone number) for this + * {@link RcsParticipant} + * @throws RcsMessageStoreException if the value could not be read from the storage */ - public String getCanonicalAddress() { - return mCanonicalAddress; + @Nullable + @WorkerThread + public String getCanonicalAddress() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantCanonicalAddress(mId)); } /** - * Sets the canonical address for this participant and updates it in storage. - * @param canonicalAddress the canonical address to update to. + * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of + * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service + * Definition Document) + * @throws RcsMessageStoreException if the value could not be read from the storage */ + @Nullable @WorkerThread - public void setCanonicalAddress(@NonNull String canonicalAddress) { - Preconditions.checkNotNull(canonicalAddress); - if (canonicalAddress.equals(mCanonicalAddress)) { - return; - } - - mCanonicalAddress = canonicalAddress; - - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re); - } + public String getAlias() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantAlias(mId)); } /** - * @return Returns the alias for this participant. Alias is usually the real name of the person - * themselves. + * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually + * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal + * Profile Service Definition Document) + * + * @param alias The alias to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage */ - public String getAlias() { - return mAlias; + @WorkerThread + public void setAlias(String alias) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantAlias(mId, alias)); } /** - * Sets the alias for this participant and persists it in storage. Alias is usually the real - * name of the person themselves. + * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for + * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device + * API 1.6 Specification) + * @throws RcsMessageStoreException if the value could not be read from the storage */ + @Nullable @WorkerThread - public void setAlias(String alias) { - if (TextUtils.equals(mAlias, alias)) { - return; - } - mAlias = alias; - - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - iRcs.updateRcsParticipantAlias(mId, mAlias); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re); - } + public String getContactId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantContactId(mId)); } /** - * Returns the row id of this participant. This is not meant to be part of the SDK + * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for + * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device + * API 1.6 Specification) * - * @hide + * @param contactId The contact ID to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage */ - public int getId() { - return mId; + @WorkerThread + public void setContactId(String contactId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantContactId(mId, contactId)); } - public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() { - @Override - public RcsParticipant createFromParcel(Parcel in) { - return new RcsParticipant(in); + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @Override - public RcsParticipant[] newArray(int size) { - return new RcsParticipant[size]; + if (!(obj instanceof RcsParticipant)) { + return false; } - }; + RcsParticipant other = (RcsParticipant) obj; - protected RcsParticipant(Parcel in) { - mId = in.readInt(); - mCanonicalAddress = in.readString(); - mAlias = in.readString(); + return mId == other.mId; } @Override - public int describeContents() { - return 0; + public int hashCode() { + return mId; } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mId); - dest.writeString(mCanonicalAddress); - dest.writeString(mAlias); + /** + * Returns the row id of this participant. This is not meant to be part of the SDK + * + * @hide + */ + public int getId() { + return mId; } } diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java index b9ca5a86f84d..aa278b38cf81 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java @@ -15,27 +15,94 @@ */ package android.telephony.ims; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; +import android.os.Parcelable; /** - * An event that indicates an {@link RcsParticipant}'s alias was changed. - * @hide - TODO(sahinc) make this public + * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA + * RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide - TODO: make public */ -public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent { +public final class RcsParticipantAliasChangedEvent extends RcsEvent implements Parcelable { + // The ID of the participant that changed their alias + private final int mParticipantId; + // The new alias of the above participant + private final String mNewAlias; + + /** + * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param participant The {@link RcsParticipant} that got their alias changed + * @param newAlias The new alias the {@link RcsParticipant} has. + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant, + @Nullable String newAlias) { + super(timestamp); + mParticipantId = participant.getId(); + mNewAlias = newAlias; + } + + /** + * @hide - internal constructor for queries + */ + public RcsParticipantAliasChangedEvent(long timestamp, int participantId, + @Nullable String newAlias) { + super(timestamp); + mParticipantId = participantId; + mNewAlias = newAlias; + } + + /** + * @return Returns the {@link RcsParticipant} whose alias was changed. + */ + @NonNull + public RcsParticipant getParticipantId() { + return new RcsParticipant(mParticipantId); + } + + /** + * @return Returns the alias of the associated {@link RcsParticipant} after this event happened + */ + @Nullable + public String getNewAlias() { + return mNewAlias; + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call(iRcs -> iRcs.createParticipantAliasChangedEvent( + getTimestamp(), getParticipantId().getId(), getNewAlias())); + } + public static final Creator<RcsParticipantAliasChangedEvent> CREATOR = new Creator<RcsParticipantAliasChangedEvent>() { - @Override - public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) { - return new RcsParticipantAliasChangedEvent(in); - } + @Override + public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) { + return new RcsParticipantAliasChangedEvent(in); + } - @Override - public RcsParticipantAliasChangedEvent[] newArray(int size) { - return new RcsParticipantAliasChangedEvent[size]; - } - }; + @Override + public RcsParticipantAliasChangedEvent[] newArray(int size) { + return new RcsParticipantAliasChangedEvent[size]; + } + }; - protected RcsParticipantAliasChangedEvent(Parcel in) { + private RcsParticipantAliasChangedEvent(Parcel in) { + super(in); + mNewAlias = in.readString(); + mParticipantId = in.readInt(); } @Override @@ -45,5 +112,8 @@ public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent { @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mNewAlias); + dest.writeInt(mParticipantId); } } diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl deleted file mode 100644 index c0a77897abd5..000000000000 --- a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsParticipantEvent; diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl new file mode 100644 index 000000000000..b7c0f93c8c5f --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsParticipantQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java new file mode 100644 index 000000000000..57c67fa7aa03 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java @@ -0,0 +1,310 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; + +/** + * The parameters to pass into + * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a + * subset of {@link RcsThread}s present in the message store. + * + * @hide - TODO: make public + */ +public final class RcsParticipantQueryParams implements Parcelable { + /** + * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of + * creation time for faster query results + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the + * {@link RcsParticipant} aliases + */ + public static final int SORT_BY_ALIAS = 1; + + /** + * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the + * {@link RcsParticipant} canonical addresses + */ + public static final int SORT_BY_CANONICAL_ADDRESS = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS}) + public @interface SortingProperty { + } + + // The SQL "like" statement to filter against participant aliases + private String mAliasLike; + // The SQL "like" statement to filter against canonical addresses + private String mCanonicalAddressLike; + // The property to sort the result against + private @SortingProperty int mSortingProperty; + // Whether to sort the result in ascending order + private boolean mIsAscending; + // The number of results to be returned from the query + private int mLimit; + // Used to limit the results to participants of a single thread + private int mThreadId; + + /** + * @hide + */ + public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters"; + + RcsParticipantQueryParams(int rcsThreadId, String aliasLike, String canonicalAddressLike, + @SortingProperty int sortingProperty, boolean isAscending, + int limit) { + mThreadId = rcsThreadId; + mAliasLike = aliasLike; + mCanonicalAddressLike = canonicalAddressLike; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + mLimit = limit; + } + + /** + * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get + * the thread that the result query should be limited to. + * + * As we do not expose any sort of integer ID's to public usage, this should be hidden. + * + * @hide - not meant for public use + */ + public int getThreadId() { + return mThreadId; + } + + /** + * @return Returns the SQL-inspired "LIKE" clause that will be used to match + * {@link RcsParticipant}s with respect to their aliases + * + * @see RcsParticipant#getAlias() + */ + public String getAliasLike() { + return mAliasLike; + } + + /** + * @return Returns the SQL-inspired "LIKE" clause that will be used to match + * {@link RcsParticipant}s with respect to their canonical addresses. + * + * @see RcsParticipant#getCanonicalAddress() + */ + public String getCanonicalAddressLike() { + return mCanonicalAddressLike; + } + + /** + * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value + * of 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property that will be used to sort the result against. + * @see SortingProperty + */ + public int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * A helper class to build the {@link RcsParticipantQueryParams}. + */ + public static class Builder { + private String mAliasLike; + private String mCanonicalAddressLike; + private @SortingProperty int mSortingProperty; + private boolean mIsAscending; + private int mLimit = 100; + private int mThreadId; + + /** + * Creates a new builder for {@link RcsParticipantQueryParams} to be used in + * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} + */ + public Builder() { + // empty implementation + } + + /** + * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread} + * + * @param rcsThread The thread that the participants should be searched in. + * @return The same {@link Builder} to chain methods. + */ + @CheckResult + public Builder setThread(RcsThread rcsThread) { + mThreadId = rcsThread.getThreadId(); + return this; + } + + /** + * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent + * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore + * ('_') wildcard matches any single character. Not using any wildcards would only perform a + * string match.The input string is case-insensitive. + * + * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie, + * while the input "An_e" would only match Anne. + * + * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases. + * @return The same {@link Builder} to chain methods + */ + @CheckResult + public Builder setAliasLike(String likeClause) { + mAliasLike = likeClause; + return this; + } + + /** + * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent + * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore + * ('_') wildcard matches any single character. Not using any wildcards would only perform a + * string match. The input string is case-insensitive. + * + * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111" + * or "+99955555111", while the input "+999_111" would only match "+9995111". + * + * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical + * addresses. + * @return The same {@link Builder} to chain methods + */ + @CheckResult + public Builder setCanonicalAddressLike(String likeClause) { + mCanonicalAddressLike = likeClause; + return this; + } + + /** + * Desired number of threads to be returned from the query. Passing in 0 will return all + * existing threads at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link RcsParticipantQueryParams.SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty against which property the results should be sorted + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Builds the {@link RcsParticipantQueryParams} to use in + * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} + * + * @return An instance of {@link RcsParticipantQueryParams} to use with the participant + * query. + */ + public RcsParticipantQueryParams build() { + return new RcsParticipantQueryParams(mThreadId, mAliasLike, mCanonicalAddressLike, + mSortingProperty, mIsAscending, mLimit); + } + } + + /** + * Parcelable boilerplate below. + */ + private RcsParticipantQueryParams(Parcel in) { + mAliasLike = in.readString(); + mCanonicalAddressLike = in.readString(); + mSortingProperty = in.readInt(); + mIsAscending = in.readByte() == 1; + mLimit = in.readInt(); + mThreadId = in.readInt(); + } + + public static final Creator<RcsParticipantQueryParams> CREATOR = + new Creator<RcsParticipantQueryParams>() { + @Override + public RcsParticipantQueryParams createFromParcel(Parcel in) { + return new RcsParticipantQueryParams(in); + } + + @Override + public RcsParticipantQueryParams[] newArray(int size) { + return new RcsParticipantQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mAliasLike); + dest.writeString(mCanonicalAddressLike); + dest.writeInt(mSortingProperty); + dest.writeByte((byte) (mIsAscending ? 1 : 0)); + dest.writeInt(mLimit); + dest.writeInt(mThreadId); + } + +} diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl new file mode 100644 index 000000000000..db5c00c8ce00 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsParticipantQueryResult; diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java new file mode 100644 index 000000000000..4eae39d1a2c6 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} + * call. This class allows getting the token for querying the next batch of participants in order to + * prevent handling large amounts of data at once. + * + * @hide - TODO: make public + */ +public final class RcsParticipantQueryResult implements Parcelable { + // A token for the caller to continue their query for the next batch of results + private RcsQueryContinuationToken mContinuationToken; + // The list of participant IDs returned with this query + private List<Integer> mParticipants; + + /** + * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create query results + * + * @hide + */ + public RcsParticipantQueryResult( + RcsQueryContinuationToken continuationToken, + List<Integer> participants) { + mContinuationToken = continuationToken; + mParticipants = participants; + } + + /** + * Returns a token to call + * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} + * to get the next batch of {@link RcsParticipant}s. + */ + @Nullable + public RcsQueryContinuationToken getContinuationToken() { + return mContinuationToken; + } + + /** + * Returns all the {@link RcsParticipant}s in the current query result. Call {@link + * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next + * batch of {@link RcsParticipant}s. + */ + @NonNull + public List<RcsParticipant> getParticipants() { + List<RcsParticipant> participantList = new ArrayList<>(); + for (Integer participantId : mParticipants) { + participantList.add(new RcsParticipant(participantId)); + } + + return participantList; + } + + private RcsParticipantQueryResult(Parcel in) { + mContinuationToken = in.readParcelable( + RcsQueryContinuationToken.class.getClassLoader()); + } + + public static final Creator<RcsParticipantQueryResult> CREATOR = + new Creator<RcsParticipantQueryResult>() { + @Override + public RcsParticipantQueryResult createFromParcel(Parcel in) { + return new RcsParticipantQueryResult(in); + } + + @Override + public RcsParticipantQueryResult[] newArray(int size) { + return new RcsParticipantQueryResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mContinuationToken, flags); + } +} diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl new file mode 100644 index 000000000000..319379a462de --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl @@ -0,0 +1,20 @@ +/* + * + * 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. + */ + +package android.telephony.ims; + +parcelable RcsQueryContinuationToken; diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java new file mode 100644 index 000000000000..c1ff39652397 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java @@ -0,0 +1,157 @@ +/* + * 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.telephony.ims; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A token for enabling continuation queries. Instances are acquired through + * {@code getContinuationToken} on result objects after initial query is done. + * + * @see RcsEventQueryResult#getContinuationToken() + * @see RcsMessageQueryResult#getContinuationToken() + * @see RcsParticipantQueryResult#getContinuationToken() + * @see RcsThreadQueryResult#getContinuationToken() + * + * @hide - TODO: make public + */ +public final class RcsQueryContinuationToken implements Parcelable { + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsEvent} queries + */ + public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; + + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsMessage} queries + */ + public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; + + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsParticipant} queries + */ + public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; + + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsThread} queries + */ + public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; + + /** + * @hide - not meant for public use + */ + public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token"; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, + PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) + public @interface ContinuationTokenType {} + + // The type of query this token should allow to continue + private @ContinuationTokenType int mQueryType; + // The raw query string for the initial query + private final String mRawQuery; + // The number of results that is returned with each query + private final int mLimit; + // The offset value that this query should start the query from + private int mOffset; + + /** + * @hide + */ + public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery, + int limit, int offset) { + mQueryType = queryType; + mRawQuery = rawQuery; + mLimit = limit; + mOffset = offset; + } + + /** + * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider} + * @hide + */ + public String getRawQuery() { + return mRawQuery; + } + + /** + * Returns which index this continuation query should start from + * @hide + */ + public int getOffset() { + return mOffset; + } + + /** + * Increments the offset by the amount of result rows returned with the continuation query for + * the next query. + * @hide + */ + public void incrementOffset() { + mOffset += mLimit; + } + + /** + * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used + * to continue. + */ + public @ContinuationTokenType int getQueryType() { + return mQueryType; + } + + private RcsQueryContinuationToken(Parcel in) { + mQueryType = in.readInt(); + mRawQuery = in.readString(); + mLimit = in.readInt(); + mOffset = in.readInt(); + } + + public static final Creator<RcsQueryContinuationToken> CREATOR = + new Creator<RcsQueryContinuationToken>() { + @Override + public RcsQueryContinuationToken createFromParcel(Parcel in) { + return new RcsQueryContinuationToken(in); + } + + @Override + public RcsQueryContinuationToken[] newArray(int size) { + return new RcsQueryContinuationToken[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mQueryType); + dest.writeString(mRawQuery); + dest.writeInt(mLimit); + dest.writeInt(mOffset); + } +} diff --git a/telephony/java/android/telephony/ims/RcsTextPart.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl deleted file mode 100644 index 4f9fe1fe26fe..000000000000 --- a/telephony/java/android/telephony/ims/RcsTextPart.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsTextPart; diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java deleted file mode 100644 index 2a72df17f32a..000000000000 --- a/telephony/java/android/telephony/ims/RcsTextPart.java +++ /dev/null @@ -1,48 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * A part of a composite {@link RcsMessage} that holds a string - * @hide - TODO(sahinc) make this public - */ -public class RcsTextPart extends RcsPart { - public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() { - @Override - public RcsTextPart createFromParcel(Parcel in) { - return new RcsTextPart(in); - } - - @Override - public RcsTextPart[] newArray(int size) { - return new RcsTextPart[size]; - } - }; - - protected RcsTextPart(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl deleted file mode 100644 index d9cf6dbc0ff0..000000000000 --- a/telephony/java/android/telephony/ims/RcsThread.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony; - -parcelable RcsThread;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java index c0a0d946d204..88655865f47b 100644 --- a/telephony/java/android/telephony/ims/RcsThread.java +++ b/telephony/java/android/telephony/ims/RcsThread.java @@ -16,60 +16,120 @@ package android.telephony.ims; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; +import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1; +import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import com.android.internal.annotations.VisibleForTesting; /** * RcsThread represents a single RCS conversation thread. It holds messages that were sent and * received and events that occurred on that thread. - * @hide - TODO(sahinc) make this public + * + * @hide - TODO: make public */ -public abstract class RcsThread implements Parcelable { - // Since this is an abstract class that gets parcelled, the sub-classes need to write these - // magic values into the parcel so that we know which type to unparcel into. - protected static final int RCS_1_TO_1_TYPE = 998; - protected static final int RCS_GROUP_TYPE = 999; - +public abstract class RcsThread { + /** + * The rcs_participant_thread_id that represents this thread in the database + * @hide + */ protected int mThreadId; + /** + * @hide + */ protected RcsThread(int threadId) { mThreadId = threadId; } - protected RcsThread(Parcel in) { - mThreadId = in.readInt(); + /** + * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an + * {@link RcsMessageSnippet} object + */ + @WorkerThread + @NonNull + public RcsMessageSnippet getSnippet() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageSnippet(mThreadId)); + } + + /** + * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage. + * + * @throws RcsMessageStoreException if the message could not be persisted into storage. + */ + @WorkerThread + @NonNull + public RcsIncomingMessage addIncomingMessage( + @NonNull RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams) + throws RcsMessageStoreException { + return new RcsIncomingMessage(RcsControllerCall.call(iRcs -> iRcs.addIncomingMessage( + mThreadId, rcsIncomingMessageCreationParams))); } - public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() { - @Override - public RcsThread createFromParcel(Parcel in) { - int type = in.readInt(); + /** + * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage. + * + * @throws RcsMessageStoreException if the message could not be persisted into storage. + */ + @WorkerThread + @NonNull + public RcsOutgoingMessage addOutgoingMessage( + @NonNull RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams) + throws RcsMessageStoreException { + int messageId = RcsControllerCall.call(iRcs -> iRcs.addOutgoingMessage( + mThreadId, rcsOutgoingMessageCreationParams)); - switch (type) { - case RCS_1_TO_1_TYPE: - return new Rcs1To1Thread(in); - case RCS_GROUP_TYPE: - return new RcsGroupThread(in); - default: - Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type); - } - return null; - } + return new RcsOutgoingMessage(messageId); + } + + /** + * Deletes an {@link RcsMessage} from this RcsThread and updates the storage. + * + * @param rcsMessage The message to delete from the thread + * @throws RcsMessageStoreException if the message could not be deleted + */ + @WorkerThread + public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.deleteMessage(rcsMessage.getId(), rcsMessage.isIncoming(), mThreadId, + isGroup())); + } + + /** + * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For + * a more detailed and paginated query, please use + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * + * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list. + * @throws RcsMessageStoreException if the messages could not be read from the storage + */ + @WorkerThread + @NonNull + public RcsMessageQueryResult getMessages() throws RcsMessageStoreException { + RcsMessageQueryParams queryParameters = + new RcsMessageQueryParams.Builder().setThread(this).build(); + return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters)); + } - @Override - public RcsThread[] newArray(int size) { - return new RcsThread[0]; - } - }; + /** + * @return Returns whether this is a group thread or not + */ + public abstract boolean isGroup(); - @Override - public int describeContents() { - return 0; + /** + * @hide + */ + @VisibleForTesting + public int getThreadId() { + return mThreadId; } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mThreadId); + /** + * @hide + */ + public int getThreadType() { + return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1; } } diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl deleted file mode 100644 index 4a40d8906bbb..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsThreadEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl deleted file mode 100644 index 82d985df4c6c..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsThreadIconChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java deleted file mode 100644 index b308fef46435..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java +++ /dev/null @@ -1,49 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an {@link RcsGroupThread}'s icon was changed. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadIconChangedEvent extends RcsThreadEvent { - public static final Creator<RcsThreadIconChangedEvent> CREATOR = - new Creator<RcsThreadIconChangedEvent>() { - @Override - public RcsThreadIconChangedEvent createFromParcel(Parcel in) { - return new RcsThreadIconChangedEvent(in); - } - - @Override - public RcsThreadIconChangedEvent[] newArray(int size) { - return new RcsThreadIconChangedEvent[size]; - } - }; - - protected RcsThreadIconChangedEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl deleted file mode 100644 index 54a311d02958..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsThreadNameChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java deleted file mode 100644 index 6f5cfdf3b4c4..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java +++ /dev/null @@ -1,49 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an {@link RcsGroupThread}'s name was changed. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadNameChangedEvent extends RcsThreadEvent { - public static final Creator<RcsThreadNameChangedEvent> CREATOR = - new Creator<RcsThreadNameChangedEvent>() { - @Override - public RcsThreadNameChangedEvent createFromParcel(Parcel in) { - return new RcsThreadNameChangedEvent(in); - } - - @Override - public RcsThreadNameChangedEvent[] newArray(int size) { - return new RcsThreadNameChangedEvent[size]; - } - }; - - protected RcsThreadNameChangedEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl deleted file mode 100644 index 047a42466ee7..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsThreadParticipantJoinedEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java deleted file mode 100644 index 5c4073c430e7..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java +++ /dev/null @@ -1,49 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an RCS participant has joined an {@link RcsGroupThread}. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent { - public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR = - new Creator<RcsThreadParticipantJoinedEvent>() { - @Override - public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) { - return new RcsThreadParticipantJoinedEvent(in); - } - - @Override - public RcsThreadParticipantJoinedEvent[] newArray(int size) { - return new RcsThreadParticipantJoinedEvent[size]; - } - }; - - protected RcsThreadParticipantJoinedEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl deleted file mode 100644 index 52f9bbd3cd93..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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. - */ - -package android.telephony.ims; - -parcelable RcsThreadParticipantLeftEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java deleted file mode 100644 index 4bf86b90ebb7..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java +++ /dev/null @@ -1,49 +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 android.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an RCS participant has left an {@link RcsGroupThread}. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadParticipantLeftEvent extends RcsThreadEvent { - public static final Creator<RcsThreadParticipantLeftEvent> CREATOR = - new Creator<RcsThreadParticipantLeftEvent>() { - @Override - public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) { - return new RcsThreadParticipantLeftEvent(in); - } - - @Override - public RcsThreadParticipantLeftEvent[] newArray(int size) { - return new RcsThreadParticipantLeftEvent[size]; - } - }; - - protected RcsThreadParticipantLeftEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java deleted file mode 100644 index 931e93dc8bf1..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telephony.ims; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to - * break large queries into manageable chunks - * @hide - TODO make this public - */ -public class RcsThreadQueryContinuationToken implements Parcelable { - protected RcsThreadQueryContinuationToken(Parcel in) { - } - - public static final Creator<RcsThreadQueryContinuationToken> CREATOR = - new Creator<RcsThreadQueryContinuationToken>() { - @Override - public RcsThreadQueryContinuationToken createFromParcel(Parcel in) { - return new RcsThreadQueryContinuationToken(in); - } - - @Override - public RcsThreadQueryContinuationToken[] newArray(int size) { - return new RcsThreadQueryContinuationToken[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl deleted file mode 100644 index feb2d4dec094..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* -** -** Copyright 2018, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package android.telephony.ims; - -parcelable RcsThreadQueryParameters; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java deleted file mode 100644 index f2c4ab1884ca..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telephony.ims; - -import android.annotation.CheckResult; -import android.os.Parcel; -import android.os.Parcelable; - -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -/** - * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in - * order to select a subset of {@link RcsThread}s present in the message store. - * @hide TODO - make the Builder and builder() public. The rest should stay internal only. - */ -public class RcsThreadQueryParameters implements Parcelable { - private final boolean mIsGroup; - private final Set<RcsParticipant> mRcsParticipants; - private final int mLimit; - private final boolean mIsAscending; - - RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit, - boolean isAscending) { - mIsGroup = isGroup; - mRcsParticipants = participants; - mLimit = limit; - mIsAscending = isAscending; - } - - /** - * Returns a new builder to build a query with. - * TODO - make public - */ - public static Builder builder() { - return new Builder(); - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get - * the list of participants. - * @hide - */ - public Set<RcsParticipant> getRcsParticipants() { - return mRcsParticipants; - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get - * whether group threads should be queried - * @hide - */ - public boolean isGroupThread() { - return mIsGroup; - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get - * the number of tuples the result query should be limited to. - */ - public int getLimit() { - return mLimit; - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to - * determine the sort order. - */ - public boolean isAscending() { - return mIsAscending; - } - - /** - * A helper class to build the {@link RcsThreadQueryParameters}. - */ - public static class Builder { - private boolean mIsGroupThread; - private Set<RcsParticipant> mParticipants; - private int mLimit = 100; - private boolean mIsAscending; - - /** - * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this, - * {@link RcsThreadQueryParameters#builder()} needs to be called. - */ - Builder() { - mParticipants = new HashSet<>(); - } - - /** - * Limits the query to only return group threads. - * @param isGroupThread Whether to limit the query result to group threads. - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder isGroupThread(boolean isGroupThread) { - mIsGroupThread = isGroupThread; - return this; - } - - /** - * Limits the query to only return threads that contain the given participant. - * @param participant The participant that must be included in all of the returned threads. - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder withParticipant(RcsParticipant participant) { - mParticipants.add(participant); - return this; - } - - /** - * Limits the query to only return threads that contain the given list of participants. - * @param participants An iterable list of participants that must be included in all of the - * returned threads. - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder withParticipants(Iterable<RcsParticipant> participants) { - for (RcsParticipant participant : participants) { - mParticipants.add(participant); - } - return this; - } - - /** - * Desired number of threads to be returned from the query. Passing in 0 will return all - * existing threads at once. The limit defaults to 100. - * @param limit The number to limit the query result to. - * @return The same instance of the builder to chain parameters. - * @throws InvalidParameterException If the given limit is negative. - */ - @CheckResult - public Builder limitResultsTo(int limit) throws InvalidParameterException { - if (limit < 0) { - throw new InvalidParameterException("The query limit must be non-negative"); - } - - mLimit = limit; - return this; - } - - /** - * Sorts the results returned from the query via thread IDs. - * - * TODO - add sorting support for other fields - * - * @param isAscending whether to sort in ascending order or not - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder sort(boolean isAscending) { - mIsAscending = isAscending; - return this; - } - - /** - * Builds the {@link RcsThreadQueryParameters} to use in - * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} - * - * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query. - */ - public RcsThreadQueryParameters build() { - return new RcsThreadQueryParameters( - mIsGroupThread, mParticipants, mLimit, mIsAscending); - } - } - - /** - * Parcelable boilerplate below. - */ - protected RcsThreadQueryParameters(Parcel in) { - mIsGroup = in.readBoolean(); - - ArrayList<RcsParticipant> participantArrayList = new ArrayList<>(); - in.readTypedList(participantArrayList, RcsParticipant.CREATOR); - mRcsParticipants = new HashSet<>(participantArrayList); - - mLimit = in.readInt(); - mIsAscending = in.readBoolean(); - } - - public static final Creator<RcsThreadQueryParameters> CREATOR = - new Creator<RcsThreadQueryParameters>() { - @Override - public RcsThreadQueryParameters createFromParcel(Parcel in) { - return new RcsThreadQueryParameters(in); - } - - @Override - public RcsThreadQueryParameters[] newArray(int size) { - return new RcsThreadQueryParameters[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeBoolean(mIsGroup); - dest.writeTypedList(new ArrayList<>(mRcsParticipants)); - dest.writeInt(mLimit); - dest.writeBoolean(mIsAscending); - } - -} diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl index eaf312877deb..3f351dc5efee 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl +++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsFileTransferPart; +parcelable RcsThreadQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java new file mode 100644 index 000000000000..ba32e320c95b --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java @@ -0,0 +1,305 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in + * order to select a subset of {@link RcsThread}s present in the message store. + * + * @hide - TODO: make public + */ +public final class RcsThreadQueryParams implements Parcelable { + /** + * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make + * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return + * {@link RcsGroupThread}s. + */ + public static final int THREAD_TYPE_GROUP = 0x0001; + + /** + * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make + * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return + * {@link Rcs1To1Thread}s. + */ + public static final int THREAD_TYPE_1_TO_1 = 0x0002; + + // The type of threads to be filtered with the query + private final int mThreadType; + // The list of participants that are expected in the resulting threads + private final List<Integer> mRcsParticipantIds; + // The number of RcsThread's that should be returned with this query + private final int mLimit; + // The property which the result of the query should be sorted against + private final @SortingProperty int mSortingProperty; + // Whether the sorting should be done in ascending + private final boolean mIsAscending; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted in the order of {@link RcsThread} creation time for faster results. + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted according to the timestamp of {@link RcsThread#getSnippet()} + */ + public static final int SORT_BY_TIMESTAMP = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP}) + public @interface SortingProperty { + } + + /** + * @hide + */ + public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters"; + + RcsThreadQueryParams(int threadType, Set<RcsParticipant> participants, + int limit, int sortingProperty, boolean isAscending) { + mThreadType = threadType; + mRcsParticipantIds = convertParticipantSetToIdList(participants); + mLimit = limit; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + } + + private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) { + List<Integer> ids = new ArrayList<>(participants.size()); + for (RcsParticipant participant : participants) { + ids.add(participant.getId()); + } + return ids; + } + + /** + * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get + * the list of participant IDs. + * + * As we don't expose any integer ID's to API users, this should stay hidden + * + * @hide - not meant for public use + */ + public List<Integer> getRcsParticipantsIds() { + return Collections.unmodifiableList(mRcsParticipantIds); + } + + /** + * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should + * return. + */ + public int getThreadType() { + return mThreadType; + } + + /** + * @return Returns the number of {@link RcsThread}s to be returned from the query. A value + * of 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property that will be used to sort the result against. + * @see SortingProperty + */ + public @SortingProperty int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * A helper class to build the {@link RcsThreadQueryParams}. + */ + public static class Builder { + private int mThreadType; + private Set<RcsParticipant> mParticipants; + private int mLimit = 100; + private @SortingProperty int mSortingProperty; + private boolean mIsAscending; + + /** + * Constructs a {@link RcsThreadQueryParams.Builder} to help build an + * {@link RcsThreadQueryParams} + */ + public Builder() { + mParticipants = new HashSet<>(); + } + + /** + * Limits the query to only return group threads. + * + * @param threadType Whether to limit the query result to group threads. + * @return The same instance of the builder to chain parameters. + * @see RcsThreadQueryParams#THREAD_TYPE_GROUP + * @see RcsThreadQueryParams#THREAD_TYPE_1_TO_1 + */ + @CheckResult + public Builder setThreadType(int threadType) { + mThreadType = threadType; + return this; + } + + /** + * Limits the query to only return threads that contain the given participant. If this + * property was not set, participants will not be taken into account while querying for + * threads. + * + * @param participant The participant that must be included in all of the returned threads. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setParticipant(@NonNull RcsParticipant participant) { + mParticipants.add(participant); + return this; + } + + /** + * Limits the query to only return threads that contain the given list of participants. If + * this property was not set, participants will not be taken into account while querying + * for threads. + * + * @param participants An iterable list of participants that must be included in all of the + * returned threads. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setParticipants(@NonNull List<RcsParticipant> participants) { + mParticipants.addAll(participants); + return this; + } + + /** + * Desired number of threads to be returned from the query. Passing in 0 will return all + * existing threads at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty whether to sort in ascending order or not + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Builds the {@link RcsThreadQueryParams} to use in + * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} + * + * @return An instance of {@link RcsThreadQueryParams} to use with the thread query. + */ + public RcsThreadQueryParams build() { + return new RcsThreadQueryParams(mThreadType, mParticipants, mLimit, + mSortingProperty, mIsAscending); + } + } + + /** + * Parcelable boilerplate below. + */ + private RcsThreadQueryParams(Parcel in) { + mThreadType = in.readInt(); + mRcsParticipantIds = new ArrayList<>(); + in.readList(mRcsParticipantIds, Integer.class.getClassLoader()); + mLimit = in.readInt(); + mSortingProperty = in.readInt(); + mIsAscending = in.readByte() == 1; + } + + public static final Creator<RcsThreadQueryParams> CREATOR = + new Creator<RcsThreadQueryParams>() { + @Override + public RcsThreadQueryParams createFromParcel(Parcel in) { + return new RcsThreadQueryParams(in); + } + + @Override + public RcsThreadQueryParams[] newArray(int size) { + return new RcsThreadQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mThreadType); + dest.writeList(mRcsParticipantIds); + dest.writeInt(mLimit); + dest.writeInt(mSortingProperty); + dest.writeByte((byte) (mIsAscending ? 1 : 0)); + } +} diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl index 4b06529d1294..b1d5cf4c7211 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl +++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl @@ -1,19 +1,19 @@ /* -** -** Copyright 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. -*/ + * + * 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. + */ package android.telephony.ims; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java index 47715f8410d6..a91126b89cce 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * 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. @@ -16,22 +16,30 @@ package android.telephony.ims; +import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1; + +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import com.android.ims.RcsTypeIdPair; + +import java.util.ArrayList; import java.util.List; /** - * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken, - * RcsThreadQueryParameters)} + * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} * call. This class allows getting the token for querying the next batch of threads in order to * prevent handling large amounts of data at once. * - * @hide + * @hide - TODO: make public */ -public class RcsThreadQueryResult implements Parcelable { - private RcsThreadQueryContinuationToken mContinuationToken; - private List<RcsThread> mRcsThreads; +public final class RcsThreadQueryResult implements Parcelable { + // A token for the caller to continue their query for the next batch of results + private RcsQueryContinuationToken mContinuationToken; + // The list of thread IDs returned with this query + private List<RcsTypeIdPair> mRcsThreadIds; /** * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} @@ -40,31 +48,47 @@ public class RcsThreadQueryResult implements Parcelable { * @hide */ public RcsThreadQueryResult( - RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) { + RcsQueryContinuationToken continuationToken, + List<RcsTypeIdPair> rcsThreadIds) { mContinuationToken = continuationToken; - mRcsThreads = rcsThreads; + mRcsThreadIds = rcsThreadIds; } /** * Returns a token to call - * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} + * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} * to get the next batch of {@link RcsThread}s. */ - public RcsThreadQueryContinuationToken nextChunkToken() { + @Nullable + public RcsQueryContinuationToken getContinuationToken() { return mContinuationToken; } /** * Returns all the RcsThreads in the current query result. Call {@link - * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of + * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of * {@link RcsThread}s. */ + @NonNull public List<RcsThread> getThreads() { - return mRcsThreads; + List<RcsThread> rcsThreads = new ArrayList<>(); + + for (RcsTypeIdPair typeIdPair : mRcsThreadIds) { + if (typeIdPair.getType() == THREAD_TYPE_1_TO_1) { + rcsThreads.add(new Rcs1To1Thread(typeIdPair.getId())); + } else { + rcsThreads.add(new RcsGroupThread(typeIdPair.getId())); + } + } + + return rcsThreads; } - protected RcsThreadQueryResult(Parcel in) { - // TODO - implement + private RcsThreadQueryResult(Parcel in) { + mContinuationToken = in.readParcelable( + RcsQueryContinuationToken.class.getClassLoader()); + mRcsThreadIds = new ArrayList<>(); + in.readList(mRcsThreadIds, Integer.class.getClassLoader()); } public static final Creator<RcsThreadQueryResult> CREATOR = @@ -87,6 +111,7 @@ public class RcsThreadQueryResult implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - // TODO - implement + dest.writeParcelable(mContinuationToken, flags); + dest.writeList(mRcsThreadIds); } } diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl index 0c958ba719f3..2478f8cff6b7 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl @@ -16,10 +16,19 @@ package android.telephony.ims.aidl; -import android.telephony.ims.RcsParticipant; -import android.telephony.ims.Rcs1To1Thread; -import android.telephony.ims.RcsThreadQueryContinuationToken; -import android.telephony.ims.RcsThreadQueryParameters; +import android.net.Uri; +import android.telephony.ims.RcsEventQueryParams; +import android.telephony.ims.RcsEventQueryResult; +import android.telephony.ims.RcsFileTransferCreationParams; +import android.telephony.ims.RcsIncomingMessageCreationParams; +import android.telephony.ims.RcsMessageSnippet; +import android.telephony.ims.RcsMessageQueryParams; +import android.telephony.ims.RcsMessageQueryResult; +import android.telephony.ims.RcsOutgoingMessageCreationParams; +import android.telephony.ims.RcsParticipantQueryParams; +import android.telephony.ims.RcsParticipantQueryResult; +import android.telephony.ims.RcsQueryContinuationToken; +import android.telephony.ims.RcsThreadQueryParams; import android.telephony.ims.RcsThreadQueryResult; /** @@ -27,23 +36,231 @@ import android.telephony.ims.RcsThreadQueryResult; * {@hide} */ interface IRcs { + ///////////////////////// // RcsMessageStore APIs - RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters); + ///////////////////////// + RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParams queryParams); RcsThreadQueryResult getRcsThreadsWithToken( - in RcsThreadQueryContinuationToken continuationToken); + in RcsQueryContinuationToken continuationToken); - void deleteThread(int threadId); + RcsParticipantQueryResult getParticipants(in RcsParticipantQueryParams queryParams); - Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant); + RcsParticipantQueryResult getParticipantsWithToken( + in RcsQueryContinuationToken continuationToken); + RcsMessageQueryResult getMessages(in RcsMessageQueryParams queryParams); + + RcsMessageQueryResult getMessagesWithToken( + in RcsQueryContinuationToken continuationToken); + + RcsEventQueryResult getEvents(in RcsEventQueryParams queryParams); + + RcsEventQueryResult getEventsWithToken( + in RcsQueryContinuationToken continuationToken); + + // returns true if the thread was successfully deleted + boolean deleteThread(int threadId, int threadType); + + // Creates an Rcs1To1Thread and returns its row ID + int createRcs1To1Thread(int participantId); + + // Creates an RcsGroupThread and returns its row ID + int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon); + + ///////////////////////// // RcsThread APIs - int getMessageCount(int rcsThreadId); + ///////////////////////// + + // Creates a new RcsIncomingMessage on the given thread and returns its row ID + int addIncomingMessage(int rcsThreadId, + in RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams); + + // Creates a new RcsOutgoingMessage on the given thread and returns its row ID + int addOutgoingMessage(int rcsThreadId, + in RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams); + + // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread + void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup); + + RcsMessageSnippet getMessageSnippet(int rcsThreadId); + + ///////////////////////// + // Rcs1To1Thread APIs + ///////////////////////// + void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId); + + long get1To1ThreadFallbackThreadId(int rcsThreadId); + + int get1To1ThreadOtherParticipantId(int rcsThreadId); + + ///////////////////////// + // RcsGroupThread APIs + ///////////////////////// + void setGroupThreadName(int rcsThreadId, String groupName); + + String getGroupThreadName(int rcsThreadId); + + void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon); + + Uri getGroupThreadIcon(int rcsThreadId); + + void setGroupThreadOwner(int rcsThreadId, int participantId); + + int getGroupThreadOwner(int rcsThreadId); + + void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri); + + Uri getGroupThreadConferenceUri(int rcsThreadId); + void addParticipantToGroupThread(int rcsThreadId, int participantId); + + void removeParticipantFromGroupThread(int rcsThreadId, int participantId); + + ///////////////////////// // RcsParticipant APIs - RcsParticipant createRcsParticipant(String canonicalAddress); + ///////////////////////// + + // Creates a new RcsParticipant and returns its rowId + int createRcsParticipant(String canonicalAddress, String alias); + + String getRcsParticipantCanonicalAddress(int participantId); + + String getRcsParticipantAlias(int participantId); + + void setRcsParticipantAlias(int id, String alias); + + String getRcsParticipantContactId(int participantId); + + void setRcsParticipantContactId(int participantId, String contactId); + + ///////////////////////// + // RcsMessage APIs + ///////////////////////// + void setMessageSubId(int messageId, boolean isIncoming, int subId); + + int getMessageSubId(int messageId, boolean isIncoming); + + void setMessageStatus(int messageId, boolean isIncoming, int status); + + int getMessageStatus(int messageId, boolean isIncoming); + + void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp); + + long getMessageOriginationTimestamp(int messageId, boolean isIncoming); + + void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId); + + String getGlobalMessageIdForMessage(int messageId, boolean isIncoming); + + void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp); + + long getMessageArrivalTimestamp(int messageId, boolean isIncoming); + + void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp); + + long getMessageSeenTimestamp(int messageId, boolean isIncoming); + + void setTextForMessage(int messageId, boolean isIncoming, String text); + + String getTextForMessage(int messageId, boolean isIncoming); + + void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude); + + double getLatitudeForMessage(int messageId, boolean isIncoming); + + void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude); + + double getLongitudeForMessage(int messageId, boolean isIncoming); + + // Returns the ID's of the file transfers attached to the given message + int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming); + + int getSenderParticipant(int messageId); + + ///////////////////////// + // RcsOutgoingMessageDelivery APIs + ///////////////////////// + + // Returns the participant ID's that this message is intended to be delivered to + int[] getMessageRecipients(int messageId); + + long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId); + + void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp); + + long getOutgoingDeliverySeenTimestamp(int messageId, int participantId); + + void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp); + + int getOutgoingDeliveryStatus(int messageId, int participantId); + + void setOutgoingDeliveryStatus(int messageId, int participantId, int status); + + ///////////////////////// + // RcsFileTransferPart APIs + ///////////////////////// + + // Performs the initial write to storage and returns the row ID. + int storeFileTransfer(int messageId, boolean isIncoming, + in RcsFileTransferCreationParams fileTransferCreationParams); + + void deleteFileTransfer(int partId); + + void setFileTransferSessionId(int partId, String sessionId); + + String getFileTransferSessionId(int partId); + + void setFileTransferContentUri(int partId, in Uri contentUri); + + Uri getFileTransferContentUri(int partId); + + void setFileTransferContentType(int partId, String contentType); + + String getFileTransferContentType(int partId); + + void setFileTransferFileSize(int partId, long fileSize); + + long getFileTransferFileSize(int partId); + + void setFileTransferTransferOffset(int partId, long transferOffset); + + long getFileTransferTransferOffset(int partId); + + void setFileTransferStatus(int partId, int transferStatus); + + int getFileTransferStatus(int partId); + + void setFileTransferWidth(int partId, int width); + + int getFileTransferWidth(int partId); + + void setFileTransferHeight(int partId, int height); + + int getFileTransferHeight(int partId); + + void setFileTransferLength(int partId, long length); + + long getFileTransferLength(int partId); + + void setFileTransferPreviewUri(int partId, in Uri uri); + + Uri getFileTransferPreviewUri(int partId); + + void setFileTransferPreviewType(int partId, String type); + + String getFileTransferPreviewType(int partId); + + ///////////////////////// + // RcsEvent APIs + ///////////////////////// + int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName); + + int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon); + + int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId); - void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress); + int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId); - void updateRcsParticipantAlias(int id, String alias); + int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias); }
\ No newline at end of file diff --git a/telephony/java/com/android/ims/RcsTypeIdPair.java b/telephony/java/com/android/ims/RcsTypeIdPair.java new file mode 100644 index 000000000000..a5177354002e --- /dev/null +++ b/telephony/java/com/android/ims/RcsTypeIdPair.java @@ -0,0 +1,79 @@ +/* + * 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.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A utility class to pass RCS IDs and types in RPC calls + * + * @hide + */ +public class RcsTypeIdPair implements Parcelable { + private int mType; + private int mId; + + public RcsTypeIdPair(int type, int id) { + mType = type; + mId = id; + } + + public int getType() { + return mType; + } + + public void setType(int type) { + mType = type; + } + + public int getId() { + return mId; + } + + public void setId(int id) { + mId = id; + } + + public RcsTypeIdPair(Parcel in) { + mType = in.readInt(); + mId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mId); + } + + public static final Creator<RcsTypeIdPair> CREATOR = + new Creator<RcsTypeIdPair>() { + @Override + public RcsTypeIdPair createFromParcel(Parcel in) { + return new RcsTypeIdPair(in); + } + + @Override + public RcsTypeIdPair[] newArray(int size) { + return new RcsTypeIdPair[size]; + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java index d3420eef518d..7478a00009c0 100644 --- a/telephony/java/com/android/internal/telephony/SmsApplication.java +++ b/telephony/java/com/android/internal/telephony/SmsApplication.java @@ -636,7 +636,7 @@ public final class SmsApplication { // Update the setting. RoleManagerCallback.Future res = new RoleManagerCallback.Future(); context.getSystemService(RoleManager.class).addRoleHolderAsUser( - RoleManager.ROLE_SMS, applicationData.mPackageName, UserHandle.of(userId), + RoleManager.ROLE_SMS, applicationData.mPackageName, 0, UserHandle.of(userId), AsyncTask.THREAD_POOL_EXECUTOR, res); try { res.get(5, TimeUnit.SECONDS); diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 964a31304db5..9080e23eb88f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -21,7 +21,6 @@ import android.os.SystemProperties; import android.telephony.PhoneNumberUtils; import android.telephony.SmsCbLocation; import android.telephony.SmsCbMessage; -import android.telephony.TelephonyManager; import android.telephony.cdma.CdmaSmsCbProgramData; import android.telephony.Rlog; import android.util.Log; @@ -746,8 +745,10 @@ public class SmsMessage extends SmsMessageBase { /** * Parses a broadcast SMS, possibly containing a CMAS alert. + * + * @param plmn the PLMN for a broadcast SMS */ - public SmsCbMessage parseBroadcastSms() { + public SmsCbMessage parseBroadcastSms(String plmn) { BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory); if (bData == null) { Rlog.w(LOG_TAG, "BearerData.decode() returned null"); @@ -758,7 +759,6 @@ public class SmsMessage extends SmsMessageBase { Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData)); } - String plmn = TelephonyManager.getDefault().getNetworkOperator(); SmsCbLocation location = new SmsCbLocation(plmn); return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2, @@ -858,11 +858,11 @@ public class SmsMessage extends SmsMessageBase { bearerData.userData = userData; byte[] encodedBearerData = BearerData.encode(bearerData); + if (encodedBearerData == null) return null; if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) { Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData); Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'"); } - if (encodedBearerData == null) return null; int teleservice = bearerData.hasUserDataHeader ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; diff --git a/test-base/api/TEST_MAPPING b/test-base/api/TEST_MAPPING new file mode 100644 index 000000000000..3535954bc019 --- /dev/null +++ b/test-base/api/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsAndroidTestBase27ApiSignatureTestCases" + } + ] +} diff --git a/test-mock/api/TEST_MAPPING b/test-mock/api/TEST_MAPPING new file mode 100644 index 000000000000..d1bd9afaf287 --- /dev/null +++ b/test-mock/api/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "CtsAndroidTestMockCurrentApiSignatureTestCases" + }, + { + "name": "CtsCurrentApiSignatureTestCases" + } + ] +} diff --git a/test-runner/api/TEST_MAPPING b/test-runner/api/TEST_MAPPING new file mode 100644 index 000000000000..76ade3c1e647 --- /dev/null +++ b/test-runner/api/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "CtsAndroidTestRunnerCurrentApiSignatureTestCases" + }, + { + "name": "CtsCurrentApiSignatureTestCases" + } + ] +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java new file mode 100644 index 000000000000..915a260f5c79 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadIconChangedEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadIconChangedEventTest { + + @Test + public void testCanUnparcel() { + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + Uri newIconUri = Uri.parse("content://new_icon"); + + RcsGroupThreadIconChangedEvent iconChangedEvent = + new RcsGroupThreadIconChangedEvent(1234567890, rcsGroupThread, rcsParticipant, + newIconUri); + + Parcel parcel = Parcel.obtain(); + iconChangedEvent.writeToParcel(parcel, iconChangedEvent.describeContents()); + + parcel.setDataPosition(0); + + iconChangedEvent = RcsGroupThreadIconChangedEvent.CREATOR.createFromParcel(parcel); + + assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri); + assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2); + assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java new file mode 100644 index 000000000000..1384c016daa8 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadNameChangedEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadNameChangedEventTest { + @Test + public void testCanUnparcel() { + String newName = "new name"; + + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + + RcsGroupThreadNameChangedEvent nameChangedEvent = + new RcsGroupThreadNameChangedEvent(1234567890, rcsGroupThread, rcsParticipant, + newName); + + Parcel parcel = Parcel.obtain(); + nameChangedEvent.writeToParcel(parcel, nameChangedEvent.describeContents()); + + parcel.setDataPosition(0); + + nameChangedEvent = RcsGroupThreadNameChangedEvent.CREATOR.createFromParcel( + parcel); + + assertThat(nameChangedEvent.getNewName()).isEqualTo(newName); + assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2); + assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java new file mode 100644 index 000000000000..d0af7db90627 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java @@ -0,0 +1,54 @@ +/* + * 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadParticipantJoinedEventTest { + + @Test + public void testCanUnparcel() { + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + + RcsGroupThreadParticipantJoinedEvent participantJoinedEvent = + new RcsGroupThreadParticipantJoinedEvent(1234567890, rcsGroupThread, rcsParticipant, + rcsParticipant); + + Parcel parcel = Parcel.obtain(); + participantJoinedEvent.writeToParcel(parcel, participantJoinedEvent.describeContents()); + + parcel.setDataPosition(0); + + participantJoinedEvent = RcsGroupThreadParticipantJoinedEvent.CREATOR.createFromParcel( + parcel); + + assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2); + assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2); + assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java new file mode 100644 index 000000000000..7ba5fa653258 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java @@ -0,0 +1,53 @@ +/* + * 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadParticipantLeftEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadParticipantLeftEventTest { + @Test + public void testCanUnparcel() { + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + + RcsGroupThreadParticipantLeftEvent participantLeftEvent = + new RcsGroupThreadParticipantLeftEvent(1234567890, rcsGroupThread, rcsParticipant, + rcsParticipant); + + Parcel parcel = Parcel.obtain(); + participantLeftEvent.writeToParcel(parcel, participantLeftEvent.describeContents()); + + parcel.setDataPosition(0); + + // create from parcel + parcel.setDataPosition(0); + participantLeftEvent = RcsGroupThreadParticipantLeftEvent.CREATOR.createFromParcel( + parcel); + assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(participantLeftEvent.getLeavingParticipantId().getId()).isEqualTo(2); + assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java deleted file mode 100644 index 44277edcdb8c..000000000000 --- a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.tests.ims; - -import android.support.test.runner.AndroidJUnit4; -import android.telephony.ims.RcsMessageStore; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class RcsMessageStoreTest { - //TODO(sahinc): Add meaningful tests once we have more of the implementation in place - @Test - public void testDeleteThreadDoesntCrash() { - RcsMessageStore mRcsMessageStore = new RcsMessageStore(); - mRcsMessageStore.deleteThread(0); - } -} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java new file mode 100644 index 000000000000..3e2bbbf8256c --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java @@ -0,0 +1,57 @@ +/* + * 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipant; +import android.telephony.ims.RcsParticipantAliasChangedEvent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsParticipantAliasChangedEventTest { + private static final String OLD_ALIAS = "old alias"; + private static final String NEW_ALIAS = "new alias"; + private RcsParticipant mParticipant; + + @Before + public void setUp() { + mParticipant = new RcsParticipant(3); + } + + @Test + public void testCanUnparcel() { + RcsParticipantAliasChangedEvent aliasChangedEvent = + new RcsParticipantAliasChangedEvent(1234567890, mParticipant, NEW_ALIAS); + + Parcel parcel = Parcel.obtain(); + aliasChangedEvent.writeToParcel(parcel, aliasChangedEvent.describeContents()); + + parcel.setDataPosition(0); + + aliasChangedEvent = RcsParticipantAliasChangedEvent.CREATOR.createFromParcel( + parcel); + + assertThat(aliasChangedEvent.getParticipantId().getId()).isEqualTo(3); + assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS); + assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java new file mode 100644 index 000000000000..6361a393187e --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java @@ -0,0 +1,57 @@ +/* + * 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipantQueryParams; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsParticipantQueryParamsTest { + + @Test + public void testCanUnparcel() { + RcsParticipantQueryParams rcsParticipantQueryParams = + new RcsParticipantQueryParams.Builder() + .setAliasLike("%alias_") + .setCanonicalAddressLike("_canonical%") + .setSortProperty(RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS) + .setSortDirection(true) + .setResultLimit(432) + .build(); + + + Parcel parcel = Parcel.obtain(); + rcsParticipantQueryParams.writeToParcel(parcel, + rcsParticipantQueryParams.describeContents()); + + parcel.setDataPosition(0); + rcsParticipantQueryParams = RcsParticipantQueryParams.CREATOR.createFromParcel( + parcel); + + assertThat(rcsParticipantQueryParams.getAliasLike()).isEqualTo("%alias_"); + assertThat(rcsParticipantQueryParams.getCanonicalAddressLike()).contains("_canonical%"); + assertThat(rcsParticipantQueryParams.getLimit()).isEqualTo(432); + assertThat(rcsParticipantQueryParams.getSortingProperty()).isEqualTo( + RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS); + assertThat(rcsParticipantQueryParams.getSortDirection()).isTrue(); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java deleted file mode 100644 index c402dbffc84b..000000000000 --- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.tests.ims; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.Bundle; -import android.support.test.runner.AndroidJUnit4; -import android.telephony.ims.RcsParticipant; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class RcsParticipantTest { - private static final int ID = 123; - private static final String ALIAS = "alias"; - private static final String CANONICAL_ADDRESS = "+1234567890"; - - @Test - public void testCanUnparcel() { - RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS); - rcsParticipant.setAlias(ALIAS); - - Bundle bundle = new Bundle(); - bundle.putParcelable("Some key", rcsParticipant); - rcsParticipant = bundle.getParcelable("Some key"); - - assertThat(rcsParticipant.getId()).isEqualTo(ID); - assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS); - assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS); - } -} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java deleted file mode 100644 index a890a389bdfc..000000000000 --- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.tests.ims; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.Bundle; -import android.support.test.runner.AndroidJUnit4; -import android.telephony.ims.RcsParticipant; -import android.telephony.ims.RcsThreadQueryParameters; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -@RunWith(AndroidJUnit4.class) -public class RcsThreadQueryParametersTest { - private RcsThreadQueryParameters mRcsThreadQueryParameters; - @Mock RcsParticipant mMockParticipant; - - @Test - public void testUnparceling() { - String key = "some key"; - mRcsThreadQueryParameters = RcsThreadQueryParameters.builder() - .isGroupThread(true) - .withParticipant(mMockParticipant) - .limitResultsTo(50) - .sort(true) - .build(); - - Bundle bundle = new Bundle(); - bundle.putParcelable(key, mRcsThreadQueryParameters); - mRcsThreadQueryParameters = bundle.getParcelable(key); - - assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue(); - assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant); - assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50); - assertThat(mRcsThreadQueryParameters.isAscending()).isTrue(); - } -} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java new file mode 100644 index 000000000000..beb4f8ad28e2 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java @@ -0,0 +1,58 @@ +/* + * 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.tests.ims; + +import static android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP; +import static android.telephony.ims.RcsThreadQueryParams.THREAD_TYPE_GROUP; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipant; +import android.telephony.ims.RcsThreadQueryParams; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsThreadQueryParamsTest { + + @Test + public void testCanUnparcel() { + RcsParticipant rcsParticipant = new RcsParticipant(1); + RcsThreadQueryParams rcsThreadQueryParams = new RcsThreadQueryParams.Builder() + .setThreadType(THREAD_TYPE_GROUP) + .setParticipant(rcsParticipant) + .setResultLimit(50) + .setSortProperty(SORT_BY_TIMESTAMP) + .setSortDirection(true) + .build(); + + Parcel parcel = Parcel.obtain(); + rcsThreadQueryParams.writeToParcel(parcel, rcsThreadQueryParams.describeContents()); + + parcel.setDataPosition(0); + rcsThreadQueryParams = RcsThreadQueryParams.CREATOR.createFromParcel(parcel); + + assertThat(rcsThreadQueryParams.getThreadType()).isEqualTo(THREAD_TYPE_GROUP); + assertThat(rcsThreadQueryParams.getRcsParticipantsIds()) + .contains(rcsParticipant.getId()); + assertThat(rcsThreadQueryParams.getLimit()).isEqualTo(50); + assertThat(rcsThreadQueryParams.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP); + assertThat(rcsThreadQueryParams.getSortDirection()).isTrue(); + } +} diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index 4b277ae850c5..f07ae9f65b1b 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -685,7 +685,7 @@ public class RollbackTest { ActivityManager am = context.getSystemService(ActivityManager.class); am.killBackgroundProcesses(TEST_APP_A); // Allow another package launch - crashQueue.offer(intent.getIntExtra("count", 0), 5, TimeUnit.SECONDS); + crashQueue.put(intent.getIntExtra("count", 0)); } catch (InterruptedException e) { fail("Failed to communicate with test app"); } @@ -694,14 +694,9 @@ public class RollbackTest { context.registerReceiver(crashCountReceiver, crashCountFilter); // Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes - Integer crashCount = null; do { RollbackTestUtils.launchPackage(TEST_APP_A); - crashCount = crashQueue.poll(5, TimeUnit.SECONDS); - if (crashCount == null) { - fail("Timed out waiting for crash signal from test app"); - } - } while(crashCount < 5); + } while(crashQueue.take() < 5); // TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.java b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java index e10baab9d8c5..c83dfa41d260 100644 --- a/telephony/java/android/telephony/ims/RcsThreadEvent.java +++ b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java @@ -13,13 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.telephony.ims; -import android.os.Parcelable; +package android.view.test; + +import android.view.ViewRootImpl; /** - * An event that happened on an {@link RcsThread}. - * @hide - TODO(sahinc) make this public + * Session to set insets mode for {@link ViewRootImpl#sNewInsetsMode}. */ -public abstract class RcsThreadEvent implements Parcelable { +public class InsetsModeSession implements AutoCloseable { + + private int mOldMode; + + public InsetsModeSession(int flag) { + mOldMode = ViewRootImpl.sNewInsetsMode; + ViewRootImpl.sNewInsetsMode = flag; + } + + @Override + public void close() throws Exception { + ViewRootImpl.sNewInsetsMode = mOldMode; + } } diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 257043b30704..a8d970edaa85 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -48,6 +48,7 @@ AtomDecl::AtomDecl(const AtomDecl& that) primaryFields(that.primaryFields), exclusiveField(that.exclusiveField), uidField(that.uidField), + whitelisted(that.whitelisted), binaryFields(that.binaryFields) {} AtomDecl::AtomDecl(int c, const string& n, const string& m) @@ -162,6 +163,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, vector<java_type_t> *signature) { int errorCount = 0; + // Build a sorted list of the fields. Descriptor has them in source file // order. map<int, const FieldDescriptor *> fields; @@ -387,6 +389,11 @@ int collate_atoms(const Descriptor *descriptor, Atoms *atoms) { const Descriptor *atom = atomField->message_type(); AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name()); + + if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) { + atomDecl.whitelisted = true; + } + vector<java_type_t> signature; errorCount += collate_atom(atom, &atomDecl, &signature); if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) { diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 450b30547c21..6b86b862dfad 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -89,6 +89,8 @@ struct AtomDecl { int uidField = 0; + bool whitelisted = false; + vector<int> binaryFields; AtomDecl(); diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 4491a8567441..0270c72ff240 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -158,6 +158,20 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, } } } + + fprintf(out, "};\n"); + fprintf(out, "\n"); + + fprintf(out, + "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n"); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); + atom != atoms.decls.end(); atom++) { + if (atom->whitelisted) { + string constant = make_constant_name(atom->name); + fprintf(out, " %s,\n", constant.c_str()); + } + } + fprintf(out, "};\n"); fprintf(out, "\n"); @@ -674,7 +688,7 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map); size_t i = 0; - // Print constants + // Print atom constants for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); atom++) { string constant = make_constant_name(atom->name); @@ -700,6 +714,30 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio fprintf(out, "};\n"); fprintf(out, "\n"); + // Print constants for the enum values. + fprintf(out, "//\n"); + fprintf(out, "// Constants for enum values\n"); + fprintf(out, "//\n\n"); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); + atom != atoms.decls.end(); atom++) { + for (vector<AtomField>::const_iterator field = atom->fields.begin(); + field != atom->fields.end(); field++) { + if (field->javaType == JAVA_TYPE_ENUM) { + fprintf(out, "// Values for %s.%s\n", atom->message.c_str(), + field->name.c_str()); + for (map<int, string>::const_iterator value = field->enumValues.begin(); + value != field->enumValues.end(); value++) { + fprintf(out, "const int32_t %s__%s__%s = %d;\n", + make_constant_name(atom->message).c_str(), + make_constant_name(field->name).c_str(), + make_constant_name(value->second).c_str(), + value->first); + } + fprintf(out, "\n"); + } + } + } + fprintf(out, "struct BytesField {\n"); fprintf(out, " BytesField(char const* array, size_t len) : arg(array), " @@ -728,6 +766,8 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio fprintf(out, " const static std::map<int, std::vector<int>> " "kBytesFieldAtoms;"); + fprintf(out, + " const static std::set<int> kWhitelistedAtoms;\n"); fprintf(out, "};\n"); fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index 3be87d95df15..24ebf4de031a 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -195,4 +195,22 @@ message GoodStateAtom3 { [(android.os.statsd.state_field_option).option = PRIMARY]; optional int32 state = 3 [(android.os.statsd.state_field_option).option = EXCLUSIVE]; +} + +message WhitelistedAtom { + optional int32 field = 1; +} + +message NonWhitelistedAtom { + optional int32 field = 1; +} + +message ListedAtoms { + oneof event { + // Atoms can be whitelisted i.e. they can be triggered by any source + WhitelistedAtom whitelisted_atom = 1 [(android.os.statsd.allow_from_any_uid) = true]; + // Atoms are not whitelisted by default, so they can only be triggered + // by whitelisted sources + NonWhitelistedAtom non_whitelisted_atom = 2; + } }
\ No newline at end of file diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index ad3bffacd442..dc585c1c5dd1 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -226,5 +226,25 @@ TEST(CollationTest, FailOnBadBinaryFieldAtom) { EXPECT_TRUE(errorCount > 0); } +TEST(CollationTest, PassOnWhitelistedAtom) { + Atoms atoms; + int errorCount = + collate_atoms(ListedAtoms::descriptor(), &atoms); + EXPECT_EQ(errorCount, 0); + EXPECT_EQ(atoms.decls.size(), 2ul); +} + +TEST(CollationTest, RecogniseWhitelistedAtom) { + Atoms atoms; + collate_atoms(ListedAtoms::descriptor(), &atoms); + for (const auto& atomDecl : atoms.decls) { + if (atomDecl.code == 1) { + EXPECT_TRUE(atomDecl.whitelisted); + } else { + EXPECT_FALSE(atomDecl.whitelisted); + } + } +} + } // namespace stats_log_api_gen } // namespace android
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index c5b9cf137dd8..3881e9e68eca 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -969,7 +969,8 @@ public class WifiConfiguration implements Parcelable { /** * Returns MAC address set to be the local randomized MAC address. - * Does not guarantee that the returned address is valid for use. + * Depending on user preference, the device may or may not use the returned MAC address for + * connections to this network. * <p> * Information is restricted to Device Owner, Profile Owner, and Carrier apps * (which will only obtain addresses for configurations which they create). Other callers diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 289f99dc9339..2cc1d8313225 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -981,7 +981,7 @@ public class WifiManager { * @deprecated This API is non-functional and will have no impact. */ @Deprecated - public static final int WIFI_MODE_FULL = 1; + public static final int WIFI_MODE_FULL = WifiProtoEnums.WIFI_MODE_FULL; // 1 /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, @@ -995,7 +995,7 @@ public class WifiManager { * @deprecated This API is non-functional and will have no impact. */ @Deprecated - public static final int WIFI_MODE_SCAN_ONLY = 2; + public static final int WIFI_MODE_SCAN_ONLY = WifiProtoEnums.WIFI_MODE_SCAN_ONLY; // 2 /** * In this Wi-Fi lock mode, Wi-Fi will not go to power save. @@ -1013,7 +1013,7 @@ public class WifiManager { * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF} * lock will have no impact. */ - public static final int WIFI_MODE_FULL_HIGH_PERF = 3; + public static final int WIFI_MODE_FULL_HIGH_PERF = WifiProtoEnums.WIFI_MODE_FULL_HIGH_PERF; // 3 /** * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency. @@ -1037,15 +1037,13 @@ public class WifiManager { * Example use cases are real time gaming or virtual reality applications where * low latency is a key factor for user experience. * <p> - * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_LOW_LATENCY} - * lock will cause the device not to go power save. - * <p> * Note: For an app which acquires both {@link #WIFI_MODE_FULL_LOW_LATENCY} and * {@link #WIFI_MODE_FULL_HIGH_PERF} locks, {@link #WIFI_MODE_FULL_LOW_LATENCY} * lock will be effective when app is running in foreground and screen is on, * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise. */ - public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; + public static final int WIFI_MODE_FULL_LOW_LATENCY = + WifiProtoEnums.WIFI_MODE_FULL_LOW_LATENCY; // 4 /** Anything worse than or equal to this will show 0 bars. */ @UnsupportedAppUsage @@ -4876,4 +4874,4 @@ public class WifiManager { throw e.rethrowFromSystemServer(); } } -}
\ No newline at end of file +} diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index a69c7a501452..333b82ccd146 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -421,7 +421,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc * .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX)) * .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"), * MacAddress.fromString("ff:ff:ff:00:00:00")) - * .buildNetworkSpecifier() + * .build() * final NetworkRequest request = * new NetworkRequest.Builder() * .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 460c633e16bd..233fa2cb4fff 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -413,17 +413,17 @@ public final class WifiNetworkSuggestion implements Parcelable { * final WifiNetworkSuggestion suggestion1 = * new Builder() * .setSsid("test111111") - * .buildNetworkSuggestion() + * .build() * final WifiNetworkSuggestion suggestion2 = * new Builder() * .setSsid("test222222") * .setWpa2Passphrase("test123456") - * .buildNetworkSuggestion() + * .build() * final WifiNetworkSuggestion suggestion3 = * new Builder() * .setSsid("test333333") * .setWpa3Passphrase("test6789") - * .buildNetworkSuggestion() + * .build() * final List<WifiNetworkSuggestion> suggestionsList = * new ArrayList<WifiNetworkSuggestion> {{ * add(suggestion1); |