diff options
1104 files changed, 23136 insertions, 7819 deletions
diff --git a/Android.bp b/Android.bp index c2024084d95d..54b6619db25e 100644 --- a/Android.bp +++ b/Android.bp @@ -305,6 +305,7 @@ java_defaults { "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl", "core/java/android/service/gatekeeper/IGateKeeperService.aidl", "core/java/android/service/contentcapture/IContentCaptureService.aidl", + "core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl", "core/java/android/service/notification/INotificationListener.aidl", "core/java/android/service/notification/IStatusBarNotificationHolder.aidl", "core/java/android/service/notification/IConditionListener.aidl", @@ -476,6 +477,7 @@ java_defaults { "media/java/android/media/IMediaRouterClient.aidl", "media/java/android/media/IMediaRouterService.aidl", "media/java/android/media/IMediaSession2.aidl", + "media/java/android/media/IMediaSession2Service.aidl", "media/java/android/media/IMediaScannerListener.aidl", "media/java/android/media/IMediaScannerService.aidl", "media/java/android/media/IPlaybackConfigDispatcher.aidl", @@ -887,7 +889,12 @@ aidl_interface { "core/java/android/net/IIpMemoryStore.aidl", "core/java/android/net/INetworkStackConnector.aidl", "core/java/android/net/INetworkStackStatusCallback.aidl", + "core/java/android/net/IpPrefixParcelable.aidl", + "core/java/android/net/LinkAddressParcelable.aidl", + "core/java/android/net/LinkPropertiesParcelable.aidl", "core/java/android/net/PrivateDnsConfigParcel.aidl", + "core/java/android/net/ProxyInfoParcelable.aidl", + "core/java/android/net/RouteInfoParcelable.aidl", "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", "core/java/android/net/dhcp/IDhcpServer.aidl", "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", @@ -1317,7 +1324,6 @@ stubs_defaults { "ext", "framework", "voip-common", - "android.test.mock.impl", ], local_sourcepaths: frameworks_base_subdirs, installable: false, diff --git a/api/current.txt b/api/current.txt index 18f150fc57d8..c027dd733a67 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5245,8 +5245,8 @@ package android.app { method public android.app.Notification clone(); method public int describeContents(); method public boolean getAllowSystemGeneratedContextualActions(); - method public android.app.PendingIntent getAppOverlayIntent(); method public int getBadgeIconType(); + method public android.app.Notification.BubbleMetadata getBubbleMetadata(); method public java.lang.String getChannelId(); method public java.lang.String getGroup(); method public int getGroupAlertBehavior(); @@ -5459,6 +5459,25 @@ package android.app { method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence); } + public static final class Notification.BubbleMetadata implements android.os.Parcelable { + method public int describeContents(); + method public int getDesiredHeight(); + method public android.graphics.drawable.Icon getIcon(); + method public android.app.PendingIntent getIntent(); + method public java.lang.CharSequence getTitle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR; + } + + public static class Notification.BubbleMetadata.Builder { + ctor public Notification.BubbleMetadata.Builder(); + method public android.app.Notification.BubbleMetadata build(); + method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int); + method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon); + method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent); + method public android.app.Notification.BubbleMetadata.Builder setTitle(java.lang.CharSequence); + } + public static class Notification.Builder { ctor public Notification.Builder(android.content.Context, java.lang.String); ctor public deprecated Notification.Builder(android.content.Context); @@ -5478,9 +5497,9 @@ package android.app { method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification); method public android.app.Notification.Builder setActions(android.app.Notification.Action...); method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean); - method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setBadgeIconType(int); + method public android.app.Notification.Builder setBubbleMetadata(android.app.Notification.BubbleMetadata); method public android.app.Notification.Builder setCategory(java.lang.String); method public android.app.Notification.Builder setChannelId(java.lang.String); method public android.app.Notification.Builder setChronometerCountDown(boolean); @@ -5695,8 +5714,8 @@ package android.app { public final class NotificationChannel implements android.os.Parcelable { ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int); + method public boolean canBubble(); method public boolean canBypassDnd(); - method public boolean canOverlayApps(); method public boolean canShowBadge(); method public int describeContents(); method public void enableLights(boolean); @@ -5712,7 +5731,7 @@ package android.app { method public android.net.Uri getSound(); method public long[] getVibrationPattern(); method public boolean hasUserSetImportance(); - method public void setAllowAppOverlay(boolean); + method public void setAllowBubbles(boolean); method public void setBypassDnd(boolean); method public void setDescription(java.lang.String); method public void setGroup(java.lang.String); @@ -5746,7 +5765,7 @@ package android.app { public class NotificationManager { method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule); - method public boolean areAppOverlaysAllowed(); + method public boolean areBubblesAllowed(); method public boolean areNotificationsEnabled(); method public boolean canNotifyAsPackage(java.lang.String); method public void cancel(int); @@ -6765,9 +6784,11 @@ package android.app.admin { method public void wipeData(int); method public void wipeData(int, java.lang.CharSequence); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; + field public static final java.lang.String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"; + field public static final java.lang.String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE"; field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"; field public static final java.lang.String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED"; field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; @@ -6810,12 +6831,15 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT"; field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER"; field public static final deprecated java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS"; + field public static final java.lang.String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI"; field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION"; field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE"; field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; + field public static final java.lang.String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; + field public static final java.lang.String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; @@ -6892,6 +6916,9 @@ package android.app.admin { field public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2; // 0x2 field public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1; // 0x1 field public static final int PRIVATE_DNS_SET_SUCCESS = 0; // 0x0 + field public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; // 0x1 + field public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; // 0x2 + field public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 3; // 0x3 field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 @@ -7663,6 +7690,7 @@ package android.app.usage { field public static final int ACTIVITY_RESUMED = 1; // 0x1 field public static final int ACTIVITY_STOPPED = 23; // 0x17 field public static final int CONFIGURATION_CHANGE = 5; // 0x5 + field public static final int DEVICE_SHUTDOWN = 26; // 0x1a field public static final int FOREGROUND_SERVICE_START = 19; // 0x13 field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14 field public static final int KEYGUARD_HIDDEN = 18; // 0x12 @@ -11922,6 +11950,9 @@ package android.content.pm { method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]); method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setLongLived(); + method public android.content.pm.ShortcutInfo.Builder setPerson(android.app.Person); + method public android.content.pm.ShortcutInfo.Builder setPersons(android.app.Person[]); method public android.content.pm.ShortcutInfo.Builder setRank(int); method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence); } @@ -12262,6 +12293,7 @@ package android.content.res { method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException; method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); + field public static final int ID_NULL = 0; // 0x0 } public static class Resources.NotFoundException extends java.lang.RuntimeException { @@ -12305,6 +12337,7 @@ package android.content.res { method public java.lang.String getPositionDescription(); method public int getResourceId(int, int); method public android.content.res.Resources getResources(); + method public int getSourceStyleResourceId(int, int); method public java.lang.String getString(int); method public java.lang.CharSequence getText(int); method public java.lang.CharSequence[] getTextArray(int); @@ -16438,9 +16471,9 @@ package android.hardware.biometrics { public class BiometricManager { method public int canAuthenticate(); - field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb + field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1 + field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc - field public static final int BIOMETRIC_ERROR_UNAVAILABLE = 1; // 0x1 field public static final int BIOMETRIC_SUCCESS = 0; // 0x0 } @@ -16483,6 +16516,7 @@ package android.hardware.biometrics { method public android.hardware.biometrics.BiometricPrompt build(); method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(java.lang.CharSequence); method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener); + method public android.hardware.biometrics.BiometricPrompt.Builder setRequireConfirmation(boolean); method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(java.lang.CharSequence); method public android.hardware.biometrics.BiometricPrompt.Builder setTitle(java.lang.CharSequence); } @@ -24116,6 +24150,7 @@ package android.media { method public int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long); method protected void finalize(); method public void flush(); + method public java.lang.String getCanonicalName(); method public android.media.MediaCodecInfo getCodecInfo(); method public java.nio.ByteBuffer getInputBuffer(int); method public deprecated java.nio.ByteBuffer[] getInputBuffers(); @@ -24242,10 +24277,15 @@ package android.media { } public final class MediaCodecInfo { + method public java.lang.String getCanonicalName(); method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String); method public java.lang.String getName(); method public java.lang.String[] getSupportedTypes(); + method public boolean isAlias(); method public boolean isEncoder(); + method public boolean isHardwareAccelerated(); + method public boolean isSoftwareOnly(); + method public boolean isVendor(); } public static final class MediaCodecInfo.AudioCapabilities { @@ -24321,7 +24361,10 @@ package android.media { field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00 field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100 field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback"; + field public static final java.lang.String FEATURE_DynamicTimestamp = "dynamic-timestamp"; + field public static final java.lang.String FEATURE_FrameParsing = "frame-parsing"; field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh"; + field public static final java.lang.String FEATURE_MultipleFrames = "multiple-frames"; field public static final java.lang.String FEATURE_PartialFrame = "partial-frame"; field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback"; field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback"; @@ -24343,6 +24386,33 @@ package android.media { field public static final int AACObjectSSR = 3; // 0x3 field public static final int AACObjectScalable = 6; // 0x6 field public static final int AACObjectXHE = 42; // 0x2a + field public static final int AV1Level2 = 1; // 0x1 + field public static final int AV1Level21 = 2; // 0x2 + field public static final int AV1Level22 = 4; // 0x4 + field public static final int AV1Level23 = 8; // 0x8 + field public static final int AV1Level3 = 16; // 0x10 + field public static final int AV1Level31 = 32; // 0x20 + field public static final int AV1Level32 = 64; // 0x40 + field public static final int AV1Level33 = 128; // 0x80 + field public static final int AV1Level4 = 256; // 0x100 + field public static final int AV1Level41 = 512; // 0x200 + field public static final int AV1Level42 = 1024; // 0x400 + field public static final int AV1Level43 = 2048; // 0x800 + field public static final int AV1Level5 = 4096; // 0x1000 + field public static final int AV1Level51 = 8192; // 0x2000 + field public static final int AV1Level52 = 16384; // 0x4000 + field public static final int AV1Level53 = 32768; // 0x8000 + field public static final int AV1Level6 = 65536; // 0x10000 + field public static final int AV1Level61 = 131072; // 0x20000 + field public static final int AV1Level62 = 262144; // 0x40000 + field public static final int AV1Level63 = 524288; // 0x80000 + field public static final int AV1Level7 = 1048576; // 0x100000 + field public static final int AV1Level71 = 2097152; // 0x200000 + field public static final int AV1Level72 = 4194304; // 0x400000 + field public static final int AV1Level73 = 8388608; // 0x800000 + field public static final int AV1Profile0 = 1; // 0x1 + field public static final int AV1Profile1 = 2; // 0x2 + field public static final int AV1Profile2 = 4; // 0x4 field public static final int AVCLevel1 = 1; // 0x1 field public static final int AVCLevel11 = 4; // 0x4 field public static final int AVCLevel12 = 8; // 0x8 @@ -24360,6 +24430,9 @@ package android.media { field public static final int AVCLevel5 = 16384; // 0x4000 field public static final int AVCLevel51 = 32768; // 0x8000 field public static final int AVCLevel52 = 65536; // 0x10000 + field public static final int AVCLevel6 = 131072; // 0x20000 + field public static final int AVCLevel61 = 262144; // 0x40000 + field public static final int AVCLevel62 = 524288; // 0x80000 field public static final int AVCProfileBaseline = 1; // 0x1 field public static final int AVCProfileConstrainedBaseline = 65536; // 0x10000 field public static final int AVCProfileConstrainedHigh = 524288; // 0x80000 @@ -24522,12 +24595,53 @@ package android.media { method public android.util.Range<java.lang.Double> getSupportedFrameRatesFor(int, int); method public android.util.Range<java.lang.Integer> getSupportedHeights(); method public android.util.Range<java.lang.Integer> getSupportedHeightsFor(int); + method public java.util.List<android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint> getSupportedPerformancePoints(); method public android.util.Range<java.lang.Integer> getSupportedWidths(); method public android.util.Range<java.lang.Integer> getSupportedWidthsFor(int); method public int getWidthAlignment(); method public boolean isSizeSupported(int, int); } + public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint { + method public boolean covers(android.media.MediaFormat); + 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; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_24; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_240; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_25; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_30; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_50; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_60; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_100; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_120; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_200; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_24; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_240; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_25; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_30; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_50; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_60; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_24; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_25; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_30; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_48; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_50; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_60; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_100; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_120; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_200; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_24; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_240; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_25; + field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_30; + 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; + } + public final class MediaCodecList { ctor public MediaCodecList(int); method public java.lang.String findDecoderForFormat(android.media.MediaFormat); @@ -24980,6 +25094,7 @@ package android.media { field public static final java.lang.String MIMETYPE_TEXT_CEA_708 = "text/cea-708"; field public static final java.lang.String MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; + field public static final java.lang.String MIMETYPE_VIDEO_AV1 = "video/av01"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision"; field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp"; @@ -25457,6 +25572,7 @@ package android.media { method public java.lang.Object setAuxEffectSendLevel(float); method public java.lang.Object setDataSource(android.media.DataSourceDesc); method public java.lang.Object setDisplay(android.view.SurfaceHolder); + method public void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback); method public java.lang.Object setNextDataSource(android.media.DataSourceDesc); method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>); method public java.lang.Object setPlaybackParams(android.media.PlaybackParams); @@ -25535,6 +25651,33 @@ package android.media { field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0 } + public static class MediaPlayer2.DrmEventCallback { + ctor public MediaPlayer2.DrmEventCallback(); + method public void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm); + method public android.media.MediaPlayer2.DrmPreparationInfo onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo); + method public byte[] onDrmKeyRequest(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm.KeyRequest); + method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int, byte[]); + } + + public static final class MediaPlayer2.DrmInfo { + method public java.util.Map<java.util.UUID, byte[]> getPssh(); + method public java.util.List<java.util.UUID> getSupportedSchemes(); + } + + public static final class MediaPlayer2.DrmPreparationInfo { + } + + public static final class MediaPlayer2.DrmPreparationInfo.Builder { + ctor public MediaPlayer2.DrmPreparationInfo.Builder(); + method public android.media.MediaPlayer2.DrmPreparationInfo build(); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setInitData(byte[]); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeySetId(byte[]); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeyType(int); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setMimeType(java.lang.String); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setOptionalParameters(java.util.Map<java.lang.String, java.lang.String>); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setUuid(java.util.UUID); + } + public static class MediaPlayer2.EventCallback { ctor public MediaPlayer2.EventCallback(); method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int); @@ -25562,6 +25705,10 @@ package android.media { field public static final java.lang.String WIDTH = "android.media.mediaplayer.width"; } + public static final class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException { + ctor public MediaPlayer2.NoDrmSchemeException(java.lang.String); + } + public static class MediaPlayer2.TrackInfo { method public android.media.MediaFormat getFormat(); method public java.lang.String getLanguage(); @@ -25653,6 +25800,7 @@ package android.media { field public static final int VOICE_CALL = 4; // 0x4 field public static final int VOICE_COMMUNICATION = 7; // 0x7 field public static final int VOICE_DOWNLINK = 3; // 0x3 + field public static final int VOICE_PERFORMANCE = 10; // 0xa field public static final int VOICE_RECOGNITION = 6; // 0x6 field public static final int VOICE_UPLINK = 2; // 0x2 } @@ -29768,7 +29916,7 @@ package android.net.wifi { method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); method public int getMaxNumberOfNetworkSuggestionsPerApp(); - method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); + method public deprecated java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); method public boolean is5GHzBandSupported(); @@ -29788,7 +29936,7 @@ package android.net.wifi { method public deprecated boolean reconnect(); method public deprecated boolean removeNetwork(int); method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>); - method public void removePasspointConfiguration(java.lang.String); + method public deprecated void removePasspointConfiguration(java.lang.String); method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); @@ -41684,6 +41832,7 @@ package android.service.voice { field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4 + field public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 128; // 0x80 field public static final int SHOW_SOURCE_NOTIFICATION = 64; // 0x40 field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20 field public static final int SHOW_WITH_ASSIST = 1; // 0x1 @@ -48200,6 +48349,7 @@ package android.util { field public int data; field public int density; field public int resourceId; + field public int sourceStyleResourceId; field public java.lang.CharSequence string; field public int type; } @@ -52961,19 +53111,8 @@ package android.view.inspector { ctor public InspectionCompanion.UninitializedPropertyMapException(); } - public final class IntEnumMapping { - method public java.lang.String nameOf(int); - } - - public static final class IntEnumMapping.Builder { - ctor public IntEnumMapping.Builder(); - method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int); - method public android.view.inspector.IntEnumMapping build(); - method public void clear(); - } - public final class IntFlagMapping { - method public java.lang.String[] namesOf(int); + method public java.util.Set<java.lang.String> get(int); } public static final class IntFlagMapping.Builder { @@ -52981,7 +53120,6 @@ package android.view.inspector { method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int); method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int); method public android.view.inspector.IntFlagMapping build(); - method public void clear(); } public abstract interface PropertyMapper { @@ -52993,7 +53131,7 @@ package android.view.inspector { method public abstract int mapFloat(java.lang.String, int); method public abstract int mapGravity(java.lang.String, int); method public abstract int mapInt(java.lang.String, int); - method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping); + method public abstract int mapIntEnum(java.lang.String, int, android.util.SparseArray<java.lang.String>); method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping); method public abstract int mapLong(java.lang.String, int); method public abstract int mapObject(java.lang.String, int); @@ -54200,6 +54338,8 @@ package android.webkit { method public static java.lang.ClassLoader getWebViewClassLoader(); method public android.webkit.WebViewClient getWebViewClient(); method public android.os.Looper getWebViewLooper(); + method public android.webkit.WebViewRenderer getWebViewRenderer(); + method public android.webkit.WebViewRendererClient getWebViewRendererClient(); method public void goBack(); method public void goBackOrForward(int); method public void goForward(); @@ -54249,6 +54389,8 @@ package android.webkit { method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); method public void setWebViewClient(android.webkit.WebViewClient); + method public void setWebViewRendererClient(java.util.concurrent.Executor, android.webkit.WebViewRendererClient); + method public void setWebViewRendererClient(android.webkit.WebViewRendererClient); method public deprecated boolean shouldDelayChildPressedState(); method public deprecated boolean showFindDialog(java.lang.String, boolean); method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>); @@ -54364,6 +54506,16 @@ package android.webkit { method public android.webkit.WebView getWebView(); } + public abstract class WebViewRenderer { + method public abstract boolean terminate(); + } + + public abstract class WebViewRendererClient { + ctor public WebViewRendererClient(); + method public abstract void onRendererResponsive(android.webkit.WebView, android.webkit.WebViewRenderer); + method public abstract void onRendererUnresponsive(android.webkit.WebView, android.webkit.WebViewRenderer); + } + } package android.widget { diff --git a/api/system-current.txt b/api/system-current.txt index 78f3471c3e86..dc5d266ac9bb 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -94,7 +94,6 @@ package android { field public static final java.lang.String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE"; field public static final java.lang.String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS"; field public static final java.lang.String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING"; - field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS"; field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; @@ -129,6 +128,7 @@ package android { field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; + field public static final java.lang.String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG"; field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS"; field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY"; @@ -151,6 +151,7 @@ package android { field public static final java.lang.String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER"; field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER"; field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; + field public static final java.lang.String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER"; field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES"; field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD"; @@ -184,6 +185,7 @@ package android { field public static final java.lang.String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; + field public static final java.lang.String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE"; field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"; field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; @@ -195,6 +197,7 @@ 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 } @@ -1559,6 +1562,7 @@ package android.content.pm { public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { field public static final int FLAG_REMOVED = 2; // 0x2 + field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000 field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 @@ -3301,6 +3305,7 @@ package android.media { } public final class MediaRecorder.AudioSource { + field public static final int ECHO_REFERENCE = 1997; // 0x7cd field public static final int HOTWORD = 1999; // 0x7cf field public static final int RADIO_TUNER = 1998; // 0x7ce } @@ -3908,24 +3913,24 @@ package android.net { package android.net.wifi { - public abstract class DppStatusCallback { - ctor public DppStatusCallback(); + public abstract class EasyConnectStatusCallback { + ctor public EasyConnectStatusCallback(); method public abstract void onConfiguratorSuccess(int); method public abstract void onEnrolleeSuccess(int); method public abstract void onFailure(int); method public abstract void onProgress(int); - field public static final int DPP_EVENT_FAILURE = -7; // 0xfffffff9 - field public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe - field public static final int DPP_EVENT_FAILURE_BUSY = -5; // 0xfffffffb - field public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc - field public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7 - field public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff - field public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd - field public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 - field public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa - field public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0 - field public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1 - field public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0 + field public static final int EASY_CONNECT_EVENT_FAILURE = -7; // 0xfffffff9 + field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe + field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb + field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc + field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7 + field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff + field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd + field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 + field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa + field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0 + field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1 + field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0 } public deprecated class RttManager { @@ -4139,12 +4144,19 @@ package android.net.wifi { field public static final int WPA2_PSK = 4; // 0x4 } + public class WifiInfo implements android.os.Parcelable { + method public boolean isOsuAp(); + } + public class WifiManager { method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); method public void connect(int, android.net.wifi.WifiManager.ActionListener); method public void disable(int, android.net.wifi.WifiManager.ActionListener); method public void disableEphemeralNetwork(java.lang.String); method public void forget(int, android.net.wifi.WifiManager.ActionListener); + method public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration, java.util.Map<java.lang.Integer, java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>); + method public java.util.Map<android.net.wifi.hotspot2.OsuProvider, java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>); + method public java.util.Map<android.net.wifi.hotspot2.OsuProvider, android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(java.util.Set<android.net.wifi.hotspot2.OsuProvider>); method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); method public android.net.wifi.WifiConfiguration getWifiApConfiguration(); method public int getWifiApState(); @@ -4156,10 +4168,11 @@ package android.net.wifi { method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); method public void setDeviceMobilityState(int); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); - method public void startDppAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.DppStatusCallback); - method public void startDppAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.DppStatusCallback); + method public void startEasyConnectAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.EasyConnectStatusCallback); + method public void startEasyConnectAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.EasyConnectStatusCallback); method public boolean startScan(android.os.WorkSource); - method public void stopDppSession(); + method public void startSubscriptionProvisioning(android.net.wifi.hotspot2.OsuProvider, android.net.wifi.hotspot2.ProvisioningCallback, android.os.Handler); + method public void stopEasyConnectSession(); method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); field public static final int CHANGE_REASON_ADDED = 0; // 0x0 field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 @@ -4169,8 +4182,8 @@ package android.net.wifi { field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2 field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3 field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0 - field public static final int DPP_NETWORK_ROLE_AP = 1; // 0x1 - field public static final int DPP_NETWORK_ROLE_STA = 0; // 0x0 + field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 + field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason"; field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; @@ -4178,6 +4191,8 @@ package android.net.wifi { field public static final java.lang.String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration"; field public static final java.lang.String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et"; field public static final java.lang.String EXTRA_WIFI_CREDENTIAL_SSID = "ssid"; + field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0 + field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1 field public static final java.lang.String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED"; field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa @@ -4354,6 +4369,59 @@ package android.net.wifi.aware { } +package android.net.wifi.hotspot2 { + + public final class OsuProvider implements android.os.Parcelable { + method public int describeContents(); + method public java.lang.String getFriendlyName(); + method public android.net.Uri getServerUri(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR; + } + + public abstract class ProvisioningCallback { + ctor public ProvisioningCallback(); + method public abstract void onProvisioningComplete(); + method public abstract void onProvisioningFailure(int); + method public abstract void onProvisioningStatus(int); + field public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; // 0x16 + field public static final int OSU_FAILURE_AP_CONNECTION = 1; // 0x1 + field public static final int OSU_FAILURE_INVALID_SERVER_URL = 8; // 0x8 + field public static final int OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17; // 0x11 + field public static final int OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21; // 0x15 + field public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; // 0xe + field public static final int OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE = 19; // 0x13 + field public static final int OSU_FAILURE_NO_PPS_MO = 16; // 0x10 + field public static final int OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE = 18; // 0x12 + field public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23; // 0x17 + field public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6; // 0x6 + field public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7; // 0x7 + field public static final int OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES = 20; // 0x14 + field public static final int OSU_FAILURE_SERVER_CONNECTION = 3; // 0x3 + field public static final int OSU_FAILURE_SERVER_URL_INVALID = 2; // 0x2 + field public static final int OSU_FAILURE_SERVER_VALIDATION = 4; // 0x4 + field public static final int OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5; // 0x5 + field public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11; // 0xb + field public static final int OSU_FAILURE_START_REDIRECT_LISTENER = 12; // 0xc + field public static final int OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13; // 0xd + field public static final int OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9; // 0x9 + field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS = 15; // 0xf + field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10; // 0xa + field public static final int OSU_STATUS_AP_CONNECTED = 2; // 0x2 + field public static final int OSU_STATUS_AP_CONNECTING = 1; // 0x1 + field public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6; // 0x6 + field public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8; // 0x8 + field public static final int OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS = 11; // 0xb + field public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9; // 0x9 + field public static final int OSU_STATUS_SERVER_CONNECTED = 5; // 0x5 + field public static final int OSU_STATUS_SERVER_CONNECTING = 3; // 0x3 + field public static final int OSU_STATUS_SERVER_VALIDATED = 4; // 0x4 + field public static final int OSU_STATUS_THIRD_SOAP_EXCHANGE = 10; // 0xa + field public static final int OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE = 7; // 0x7 + } + +} + package android.net.wifi.rtt { public static final class RangingRequest.Builder { @@ -4445,6 +4513,10 @@ package android.os { method public abstract java.lang.Object onTransactStarted(android.os.IBinder, int); } + public static class Build.VERSION { + field public static final java.lang.String PREVIEW_SDK_FINGERPRINT; + } + public final class ConfigUpdate { field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB"; field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS"; @@ -4863,6 +4935,8 @@ package android.permission { method public final android.os.IBinder onBind(android.content.Intent); method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean); method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String); + method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream); + method public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onPermissionUsageResult(boolean, long); method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String); method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String); field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; @@ -4891,11 +4965,12 @@ package android.permission { public final class RuntimePermissionUsageInfo implements android.os.Parcelable { ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int); method public int describeContents(); - method public java.lang.CharSequence getName(); method public int getAppAccessCount(); + method public java.lang.CharSequence getName(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR; } + } package android.permissionpresenterservice { @@ -5017,6 +5092,7 @@ package android.provider { method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener); method public static void resetToDefaults(int, java.lang.String); method public static boolean setProperty(java.lang.String, java.lang.String, java.lang.String, boolean); + field public static final java.lang.String NAMESPACE_GAME_DRIVER = "game_driver"; field public static final java.lang.String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot"; } @@ -7179,6 +7255,7 @@ package android.telephony.ims { method public int getServiceType(); method public static int getVideoStateFromCallType(int); method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile); + method public boolean isEmergencyCallTesting(); method public boolean isVideoCall(); method public boolean isVideoPaused(); method public static int presentationToOir(int); @@ -7187,6 +7264,7 @@ package android.telephony.ims { method public void setCallExtraInt(java.lang.String, int); method public void setCallRestrictCause(int); method public void setEmergencyCallRouting(int); + method public void setEmergencyCallTesting(boolean); method public void setEmergencyServiceCategories(int); method public void setEmergencyUrns(java.util.List<java.lang.String>); method public void updateCallExtras(android.telephony.ims.ImsCallProfile); @@ -7251,7 +7329,6 @@ package android.telephony.ims { method public void callSessionInviteParticipantsRequestDelivered(); method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMayHandover(int, int); - method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase); method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); @@ -7262,6 +7339,7 @@ package android.telephony.ims { method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile); method public void callSessionResumed(android.telephony.ims.ImsCallProfile); + method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionRttMessageReceived(java.lang.String); method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile); method public void callSessionRttModifyResponseReceived(int); @@ -8492,6 +8570,8 @@ package android.webkit { method public abstract int getVisibleTitleHeight(); method public abstract android.webkit.WebChromeClient getWebChromeClient(); method public abstract android.webkit.WebViewClient getWebViewClient(); + method public abstract android.webkit.WebViewRenderer getWebViewRenderer(); + method public abstract android.webkit.WebViewRendererClient getWebViewRendererClient(); method public abstract android.view.View getZoomControls(); method public abstract void goBack(); method public abstract void goBackOrForward(int); @@ -8541,6 +8621,7 @@ package android.webkit { method public abstract void setVerticalScrollbarOverlay(boolean); method public abstract void setWebChromeClient(android.webkit.WebChromeClient); method public abstract void setWebViewClient(android.webkit.WebViewClient); + method public abstract void setWebViewRendererClient(java.util.concurrent.Executor, android.webkit.WebViewRendererClient); method public abstract boolean showFindDialog(java.lang.String, boolean); method public abstract void stopLoading(); method public abstract boolean zoomBy(float); diff --git a/api/system-removed.txt b/api/system-removed.txt index 4beb69920e28..5a7ec8bd09bd 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -1,3 +1,11 @@ +package android { + + public static final class Manifest.permission { + field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; + } + +} + package android.app { public class Notification implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index fb50fa194a92..e0e0f3506ad4 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -420,6 +420,7 @@ package android.content.pm { } public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000 field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 @@ -509,6 +510,11 @@ package android.graphics { method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int); } + public class Paint { + method public void setColor(long); + method public void setShadowLayer(float, float, float, long); + } + } package android.graphics.drawable { @@ -574,6 +580,8 @@ package android.hardware.display { public final class BrightnessConfiguration implements android.os.Parcelable { method public int describeContents(); + method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int); + method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String); method public android.util.Pair<float[], float[]> getCurve(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR; @@ -581,10 +589,22 @@ package android.hardware.display { public static class BrightnessConfiguration.Builder { ctor public BrightnessConfiguration.Builder(float[], float[]); + method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection); + method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection); method public android.hardware.display.BrightnessConfiguration build(); + method public int getMaxCorrectionsByCategory(); + method public int getMaxCorrectionsByPackageName(); method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String); } + public final class BrightnessCorrection implements android.os.Parcelable { + method public float apply(float); + method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR; + } + public final class DisplayManager { method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats(); method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration(); @@ -945,6 +965,11 @@ package android.os { field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR; } + public final class MessageQueue { + method public int postSyncBarrier(); + method public void removeSyncBarrier(int); + } + public final class NativeHandle implements java.io.Closeable { ctor public NativeHandle(); ctor public NativeHandle(java.io.FileDescriptor, boolean); @@ -957,11 +982,6 @@ package android.os { method public boolean hasSingleFileDescriptor(); } - public final class MessageQueue { - method public int postSyncBarrier(); - method public void removeSyncBarrier(int); - } - public final class PowerManager { method public int getPowerSaveMode(); method public boolean setDynamicPowerSavings(boolean, int); @@ -1098,6 +1118,24 @@ package android.os { field public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR; } + public class VintfObject { + method public static java.lang.String[] getHalNamesAndVersions(); + method public static java.lang.String getSepolicyVersion(); + method public static java.lang.Long getTargetFrameworkCompatibilityMatrixVersion(); + method public static java.util.Map<java.lang.String, java.lang.String[]> getVndkSnapshots(); + method public static java.lang.String[] report(); + } + + public class VintfRuntimeInfo { + method public static java.lang.String getCpuInfo(); + method public static java.lang.String getHardwareId(); + method public static java.lang.String getKernelVersion(); + method public static java.lang.String getNodeName(); + method public static java.lang.String getOsName(); + method public static java.lang.String getOsRelease(); + method public static java.lang.String getOsVersion(); + } + public class WorkSource implements android.os.Parcelable { ctor public WorkSource(int); method public boolean add(int); diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index abe18ba8a415..803f83c0bc6f 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -20,6 +20,7 @@ cc_defaults { "misc-*", "modernize-*", "readability-*", + "-modernize-avoid-c-arrays", ], tidy_flags: [ "-system-headers", @@ -38,6 +39,7 @@ cc_library { "libidmap2/CommandLineOptions.cpp", "libidmap2/FileUtils.cpp", "libidmap2/Idmap.cpp", + "libidmap2/Policies.cpp", "libidmap2/PrettyPrintVisitor.cpp", "libidmap2/RawPrintVisitor.cpp", "libidmap2/ResourceUtils.cpp", @@ -87,6 +89,7 @@ cc_test { "tests/Idmap2BinaryTests.cpp", "tests/IdmapTests.cpp", "tests/Main.cpp", + "tests/PoliciesTests.cpp", "tests/PrettyPrintVisitorTests.cpp", "tests/RawPrintVisitorTests.cpp", "tests/ResourceUtilsTests.cpp", diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index b07567331e85..c455ac0f83af 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -27,17 +27,25 @@ #include "idmap2/CommandLineOptions.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" +#include "idmap2/Policies.h" +#include "idmap2/Result.h" using android::ApkAssets; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Idmap; +using android::idmap2::PoliciesToBitmask; +using android::idmap2::PolicyBitmask; +using android::idmap2::PolicyFlags; +using android::idmap2::Result; using android::idmap2::utils::kIdmapFilePermissionMask; bool Create(const std::vector<std::string>& args, std::ostream& out_error) { std::string target_apk_path; std::string overlay_apk_path; std::string idmap_path; + std::vector<std::string> policies; + bool ignore_overlayable; const CommandLineOptions opts = CommandLineOptions("idmap2 create") @@ -47,12 +55,28 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) { .MandatoryOption("--overlay-apk-path", "input: path to apk which contains the new resource values", &overlay_apk_path) - .MandatoryOption("--idmap-path", "output: path to where to write idmap file", - &idmap_path); + .MandatoryOption("--idmap-path", "output: path to where to write idmap file", &idmap_path) + .OptionalOption("--policy", + "input: an overlayable policy this overlay fulfills " + "(if none or supplied, the overlay policy will default to \"public\")", + &policies) + .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks", + &ignore_overlayable); if (!opts.Parse(args, out_error)) { return false; } + PolicyBitmask fulfilled_policies = 0; + if (auto result = PoliciesToBitmask(policies, out_error)) { + fulfilled_policies |= *result; + } else { + return false; + } + + if (fulfilled_policies == 0) { + fulfilled_policies |= PolicyFlags::POLICY_PUBLIC; + } + const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); if (!target_apk) { out_error << "error: failed to load apk " << target_apk_path << std::endl; @@ -66,7 +90,8 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) { } const std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, out_error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + fulfilled_policies, !ignore_overlayable, out_error); if (!idmap) { return false; } diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 4f88127f8af1..a269ee958497 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -21,8 +21,11 @@ #include <set> #include <sstream> #include <string> +#include <utility> #include <vector> +#include "android-base/properties.h" + #include "idmap2/CommandLineOptions.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" @@ -34,6 +37,10 @@ using android::idmap2::CommandLineOptions; using android::idmap2::Idmap; using android::idmap2::MemoryChunk; +using android::idmap2::PoliciesToBitmask; +using android::idmap2::PolicyBitmask; +using android::idmap2::PolicyFlags; +using android::idmap2::Result; using android::idmap2::Xml; using android::idmap2::ZipFile; using android::idmap2::utils::FindFiles; @@ -45,11 +52,19 @@ struct InputOverlay { return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path); } - std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes) - std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes) - int priority; // NOLINT(misc-non-private-member-variables-in-classes) + std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes) + std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes) + int priority; // NOLINT(misc-non-private-member-variables-in-classes) + std::vector<std::string> policies; // NOLINT(misc-non-private-member-variables-in-classes) + bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes) }; +bool VendorIsQOrLater() { + // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized + std::string version = android::base::GetProperty("ro.vndk.version", "Q"); + return version == "Q" || version == "q"; +} + std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs, bool recursive, std::ostream& out_error) { const auto predicate = [](unsigned char type, const std::string& path) -> bool { @@ -70,6 +85,22 @@ std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::st return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend()); } +PolicyBitmask PolicyForPath(const std::string& apk_path) { + static const std::vector<std::pair<std::string, PolicyBitmask>> values = { + {"/product/", PolicyFlags::POLICY_PRODUCT_PARTITION}, + {"/system/", PolicyFlags::POLICY_SYSTEM_PARTITION}, + {"/vendor/", PolicyFlags::POLICY_VENDOR_PARTITION}, + }; + + for (auto const& pair : values) { + if (apk_path.compare(0, pair.first.size(), pair.first) == 0) { + return pair.second | PolicyFlags::POLICY_PUBLIC; + } + } + + return PolicyFlags::POLICY_PUBLIC; +} + } // namespace bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { @@ -77,6 +108,7 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { std::string target_package_name; std::string target_apk_path; std::string output_directory; + std::vector<std::string> override_policies; bool recursive = false; const CommandLineOptions opts = @@ -89,7 +121,12 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path) .MandatoryOption("--output-directory", "directory in which to write artifacts (idmap files and overlays.list)", - &output_directory); + &output_directory) + .OptionalOption( + "--override-policy", + "input: an overlayable policy this overlay fulfills " + "(if none or supplied, the overlays will not have their policies overriden", + &override_policies); if (!opts.Parse(args, out_error)) { return false; } @@ -144,29 +181,63 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { continue; } - // Sort the static overlays in ascending priority order + PolicyBitmask fulfilled_policies; + if (!override_policies.empty()) { + if (Result<PolicyBitmask> result = PoliciesToBitmask(override_policies, out_error)) { + fulfilled_policies = *result; + } else { + return false; + } + } else { + fulfilled_policies = PolicyForPath(path); + } + + bool ignore_overlayable = false; + if ((fulfilled_policies & PolicyFlags::POLICY_VENDOR_PARTITION) != 0 && !VendorIsQOrLater()) { + // If the overlay is on a pre-Q vendor partition, do not enforce overlayable + // restrictions on this overlay because the pre-Q platform has no understanding of + // overlayable. + ignore_overlayable = true; + } + std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path); - InputOverlay input{path, idmap_path, priority}; + + // Sort the static overlays in ascending priority order + InputOverlay input{path, idmap_path, priority, override_policies, ignore_overlayable}; interesting_apks.insert( std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input); } std::stringstream stream; for (const auto& overlay : interesting_apks) { + // Create the idmap for the overlay if it currently does not exist or if it is not up to date. std::stringstream dev_null; - if (!Verify(std::vector<std::string>({"--idmap-path", overlay.idmap_path}), dev_null) && - !Create(std::vector<std::string>({ - "--target-apk-path", - target_apk_path, - "--overlay-apk-path", - overlay.apk_path, - "--idmap-path", - overlay.idmap_path, - }), - out_error)) { - return false; + + std::vector<std::string> verify_args = {"--idmap-path", overlay.idmap_path}; + for (const std::string& policy : overlay.policies) { + verify_args.emplace_back("--policy"); + verify_args.emplace_back(policy); } - stream << overlay.idmap_path << std::endl; + + if (!Verify(std::vector<std::string>(verify_args), dev_null)) { + std::vector<std::string> create_args = {"--target-apk-path", target_apk_path, + "--overlay-apk-path", overlay.apk_path, + "--idmap-path", overlay.idmap_path}; + if (overlay.ignore_overlayable) { + create_args.emplace_back("--ignore-overlayable"); + } + + for (const std::string& policy : overlay.policies) { + verify_args.emplace_back("--policy"); + verify_args.emplace_back(policy); + } + + if (!Create(create_args, out_error)) { + return false; + } + } + + stream << overlay.idmap_path << std::endl; } std::cout << stream.str(); diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index d2e46e15fc59..a3c752718ee2 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -26,12 +26,15 @@ #include <string> #include "android-base/macros.h" +#include "android-base/stringprintf.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 "idmap2d/Idmap2Service.h" @@ -39,6 +42,8 @@ using android::binder::Status; using android::idmap2::BinaryStreamVisitor; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; +using android::idmap2::PolicyBitmask; +using android::idmap2::Result; using android::idmap2::utils::kIdmapFilePermissionMask; namespace { @@ -54,6 +59,10 @@ Status error(const std::string& msg) { return Status::fromExceptionCode(Status::EX_NONE, msg.c_str()); } +PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) { + return static_cast<PolicyBitmask>(arg); +} + } // namespace namespace android::os { @@ -78,6 +87,8 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, } 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) { assert(_aidl_return); const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); @@ -86,11 +97,15 @@ Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, fin.close(); std::stringstream dev_null; *_aidl_return = header && header->IsUpToDate(dev_null); + + // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed + return ok(); } Status Idmap2Service::createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t user_id, + const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id, std::unique_ptr<std::string>* _aidl_return) { assert(_aidl_return); std::stringstream trace; @@ -101,6 +116,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, _aidl_return->reset(nullptr); + const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies); + const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); if (!target_apk) { return error("failed to load apk " + target_apk_path); @@ -113,7 +130,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, std::stringstream err; const std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, err); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + policy_bitmask, enforce_overlayable, err); if (!idmap) { return error(err.str()); } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index e0bc22e9e9e6..1aab0598449b 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -39,11 +39,12 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id, bool* _aidl_return); - binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id, - bool* _aidl_return); + binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id, bool* _aidl_return); binder::Status createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t user_id, + const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id, std::unique_ptr<std::string>* _aidl_return); }; diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl index d475417a0935..ea7274f3ea58 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl @@ -20,9 +20,18 @@ package android.os; * @hide */ interface IIdmap2 { + const int POLICY_PUBLIC = 0x00000001; + const int POLICY_SYSTEM_PARTITION = 0x00000002; + const int POLICY_VENDOR_PARTITION = 0x00000004; + const int POLICY_PRODUCT_PARTITION = 0x00000008; + @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId); boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId); - boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId); + boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies, + boolean enforceOverlayable, int userId); @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath, - @utf8InCpp String overlayApkPath, int userId); + @utf8InCpp String overlayApkPath, + int fulfilledPolicies, + boolean enforceOverlayable, + int userId); } diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h index b93e71631d08..6db6bf9ea8ad 100644 --- a/cmds/idmap2/include/idmap2/CommandLineOptions.h +++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h @@ -44,6 +44,8 @@ class CommandLineOptions { std::vector<std::string>* value); CommandLineOptions& OptionalOption(const std::string& name, const std::string& description, std::string* value); + CommandLineOptions& OptionalOption(const std::string& name, const std::string& description, + std::vector<std::string>* value); bool Parse(const std::vector<std::string>& argv, std::ostream& outError) const; void Usage(std::ostream& out) const; @@ -56,6 +58,7 @@ class CommandLineOptions { COUNT_OPTIONAL, COUNT_EXACTLY_ONCE, COUNT_ONCE_OR_MORE, + COUNT_OPTIONAL_ONCE_OR_MORE, } count; bool argument; }; diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index b989e4c3f9d1..1666dc8a3bbd 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -57,6 +57,8 @@ #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" +#include "idmap2/Policies.h" + namespace android::idmap2 { class Idmap; @@ -233,11 +235,10 @@ class Idmap { // file is used; change this in the next version of idmap to use a named // package instead; also update FromApkAssets to take additional parameters: // the target and overlay package names - static std::unique_ptr<const Idmap> FromApkAssets(const std::string& target_apk_path, - const ApkAssets& target_apk_assets, - const std::string& overlay_apk_path, - const ApkAssets& overlay_apk_assets, - std::ostream& out_error); + static std::unique_ptr<const 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); inline const std::unique_ptr<const IdmapHeader>& GetHeader() const { return header_; diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h new file mode 100644 index 000000000000..eecee25445e2 --- /dev/null +++ b/cmds/idmap2/include/idmap2/Policies.h @@ -0,0 +1,41 @@ +/* + * 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 <ostream> +#include <string> +#include <vector> + +#include "androidfw/ResourceTypes.h" +#include "androidfw/StringPiece.h" + +#include "Result.h" + +#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ +#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ + +namespace android::idmap2 { + +using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags; +using PolicyBitmask = uint32_t; + +// Parses a the string representation of a set of policies into a bitmask. The format of the string +// is the same as for the <policy> element. +Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies, + std::ostream& err); + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp index cabc8f398c46..a49a607091a4 100644 --- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp +++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp @@ -68,9 +68,18 @@ CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name, return *this; } +CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name, + const std::string& description, + std::vector<std::string>* value) { + assert(value != nullptr); + auto func = [value](const std::string& arg) -> void { value->push_back(arg); }; + options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true}); + return *this; +} + bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const { const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) { - return opt.count != Option::COUNT_OPTIONAL; + return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE; }); std::set<std::string> mandatory_opts; std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()), @@ -122,7 +131,8 @@ void CommandLineOptions::Usage(std::ostream& out) const { size_t maxLength = 0; out << "usage: " << name_; for (const Option& opt : options_) { - const bool mandatory = opt.count != Option::COUNT_OPTIONAL; + const bool mandatory = + opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE; out << " "; if (!mandatory) { out << "["; @@ -134,9 +144,15 @@ void CommandLineOptions::Usage(std::ostream& out) const { out << opt.name; maxLength = std::max(maxLength, opt.name.size()); } + + if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) { + out << " [..]"; + } + if (!mandatory) { out << "]"; } + if (opt.count == Option::COUNT_ONCE_OR_MORE) { out << " [" << opt.name << " arg [..]]"; } @@ -150,7 +166,8 @@ void CommandLineOptions::Usage(std::ostream& out) const { out << opt.name; } out << " " << opt.description; - if (opt.count == Option::COUNT_ONCE_OR_MORE) { + if (opt.count == Option::COUNT_ONCE_OR_MORE || + opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) { out << " (can be provided multiple times)"; } out << std::endl; diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 37d6af8fa477..2890ae11b0af 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -274,11 +274,23 @@ std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream, return std::move(idmap); } -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, - std::ostream& out_error) { +bool CheckOverlayable(const LoadedPackage& target_package, PolicyBitmask fulfilled_polices, + ResourceId resid) { + const OverlayableInfo* info = target_package.GetOverlayableInfo(resid); + if (info == nullptr) { + // If the resource does not have an overlayable definition, allow the resource to be overlaid. + // Once overlayable enforcement is turned on, this check will return false. + return true; + } + + // Enforce policy restrictions if the resource is declared as overlayable. + return (info->policy_flags & fulfilled_polices) != 0; +} + +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) { 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; @@ -380,6 +392,15 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_ if (target_resid == 0) { continue; } + + if (enforce_overlayable && !CheckOverlayable(*target_pkg, fulfilled_policies, target_resid)) { + // The resources must be defined as overlayable and the overlay must fulfill at least one + // policy enforced on the overlayable resource + LOG(WARNING) << "overlay \"" << overlay_apk_path << "\" is not allowed to overlay resource \"" + << full_name << "\"" << std::endl; + continue; + } + matching_resources.Add(target_resid, overlay_resid); } diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp new file mode 100644 index 000000000000..0f87ef0c4ea9 --- /dev/null +++ b/cmds/idmap2/libidmap2/Policies.cpp @@ -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. + */ + +#include <iterator> +#include <map> +#include <sstream> +#include <string> +#include <vector> + +#include "androidfw/ResourceTypes.h" + +#include "idmap2/Idmap.h" +#include "idmap2/Policies.h" +#include "idmap2/Result.h" + +namespace android::idmap2 { + +namespace { + +const std::map<android::StringPiece, PolicyFlags> kStringToFlag = { + {"public", PolicyFlags::POLICY_PUBLIC}, + {"product", PolicyFlags::POLICY_PRODUCT_PARTITION}, + {"system", PolicyFlags::POLICY_SYSTEM_PARTITION}, + {"vendor", PolicyFlags::POLICY_VENDOR_PARTITION}, +}; +} // namespace + +Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies, + std::ostream& err) { + PolicyBitmask bitmask = 0; + for (const std::string& policy : policies) { + const auto iter = kStringToFlag.find(policy); + if (iter != kStringToFlag.end()) { + bitmask |= iter->second; + } else { + err << "error: unknown policy \"" << policy << "\""; + return kResultError; + } + } + + return Result<PolicyBitmask>(bitmask); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 2698ac0a734d..35ec1ff1dbb5 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -78,7 +78,8 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { std::stringstream error; std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; @@ -101,25 +102,52 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { header = loaded_idmap->GetEntryMapForType(0x02); ASSERT_THAT(header, NotNull()); - success = LoadedIdmap::Lookup(header, 0x0002, &entry); + success = LoadedIdmap::Lookup(header, 0x0000, &entry); // string/a ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x0003, &entry); + success = LoadedIdmap::Lookup(header, 0x0001, &entry); // string/b + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0002, &entry); // string/c + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0003, &entry); // string/not_overlayable + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0004, &entry); // string/policy_product + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0005, &entry); // string/policy_public + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0006, &entry); // string/policy_system + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0007, &entry); // string/policy_system_vendor + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/str1 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0000); - success = LoadedIdmap::Lookup(header, 0x0004, &entry); + success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/str2 ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x0005, &entry); + success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str3 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0001); - success = LoadedIdmap::Lookup(header, 0x0006, &entry); + success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str4 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0002); - success = LoadedIdmap::Lookup(header, 0x0007, &entry); + success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/x + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/y + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/z ASSERT_FALSE(success); } diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp index c27d27a16b94..39f18d3336af 100644 --- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp +++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp @@ -121,6 +121,56 @@ TEST(CommandLineOptionsTests, OptionalOption) { ASSERT_FALSE(success); } +TEST(CommandLineOptionsTests, OptionalOptionList) { + std::vector<std::string> foo; + std::vector<std::string> bar; + CommandLineOptions opts = CommandLineOptions("test") + .OptionalOption("--foo", "", &foo) + .OptionalOption("--bar", "", &bar); + std::ostream fakeStdErr(nullptr); + bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr); + ASSERT_TRUE(success); + ASSERT_EQ(foo.size(), 1U); + ASSERT_EQ(foo[0], "FOO"); + ASSERT_EQ(bar.size(), 1U); + ASSERT_EQ(bar[0], "BAR"); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo", "BAZ"}, fakeStdErr); + ASSERT_TRUE(success); + ASSERT_EQ(foo.size(), 1U); + ASSERT_EQ(foo[0], "BAZ"); + ASSERT_EQ(bar.size(), 0U); + + foo.clear(); + bar.clear(); + success = + opts.Parse({"--foo", "BAZ", "--foo", "BIZ", "--bar", "FIZ", "--bar", "FUZZ"}, fakeStdErr); + ASSERT_TRUE(success); + ASSERT_EQ(foo.size(), 2U); + ASSERT_EQ(foo[0], "BAZ"); + ASSERT_EQ(foo[1], "BIZ"); + ASSERT_EQ(bar.size(), 2U); + ASSERT_EQ(bar[0], "FIZ"); + ASSERT_EQ(bar[1], "FUZZ"); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo"}, fakeStdErr); + ASSERT_FALSE(success); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr); + ASSERT_FALSE(success); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr); + ASSERT_FALSE(success); +} + TEST(CommandLineOptionsTests, CornerCases) { std::string foo; std::string bar; @@ -172,6 +222,7 @@ TEST(CommandLineOptionsTests, Usage) { bool arg5 = false; bool arg6 = false; std::vector<std::string> arg7; + std::vector<std::string> arg8; CommandLineOptions opts = CommandLineOptions("test") .MandatoryOption("--aa", "description-aa", &arg1) .OptionalFlag("--bb", "description-bb", &arg5) @@ -179,12 +230,13 @@ TEST(CommandLineOptionsTests, Usage) { .OptionalOption("--dd", "description-dd", &arg3) .MandatoryOption("--ee", "description-ee", &arg4) .OptionalFlag("--ff", "description-ff", &arg6) - .MandatoryOption("--gg", "description-gg", &arg7); + .MandatoryOption("--gg", "description-gg", &arg7) + .OptionalOption("--hh", "description-hh", &arg8); std::stringstream stream; opts.Usage(stream); const std::string s = stream.str(); ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg " - "[--gg arg [..]]"), + "[--gg arg [..]] [--hh arg [..]]"), std::string::npos); ASSERT_NE(s.find("--aa arg description-aa"), std::string::npos); ASSERT_NE(s.find("--ff description-ff"), std::string::npos); diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 4bf832a34691..d9d9a7f829cf 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -37,10 +37,10 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { [](unsigned char type ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 4U); - ASSERT_EQ( - std::set<std::string>(v->begin(), v->end()), - std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"})); + ASSERT_EQ(v->size(), 6U); + ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), + std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target", + root + "/system-overlay", root + "/system-overlay-invalid"})); } TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { @@ -49,11 +49,13 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 4U); + ASSERT_EQ(v->size(), 6U); ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk", root + "/overlay/overlay-static-1.apk", - root + "/overlay/overlay-static-2.apk"})); + root + "/overlay/overlay-static-2.apk", + root + "/system-overlay/system-overlay.apk", + root + "/system-overlay-invalid/system-overlay-invalid.apk"})); } TEST(FileUtilsTests, ReadFile) { diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 22f48e9a396f..0c8f164bf096 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -38,6 +38,7 @@ #include "gtest/gtest.h" #include "androidfw/PosixUtils.h" + #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" @@ -114,8 +115,9 @@ TEST_F(Idmap2BinaryTests, Dump) { ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f020008 -> 0x7f020000 string/str1"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020001 string/str3"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020002 string/str4"), std::string::npos); ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos); // clang-format off @@ -157,7 +159,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--recursive", "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -190,7 +193,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--input-directory", GetTestDataPath() + "/overlay", "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -207,7 +211,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--recursive", "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -222,7 +227,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--input-directory", GetTempDirPath(), "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -245,7 +251,7 @@ TEST_F(Idmap2BinaryTests, Lookup) { "lookup", "--idmap-path", GetIdmapPath(), "--config", "", - "--resid", "0x7f020003"}); // string/str1 + "--resid", "0x7f020008"}); // string/str1 // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -310,6 +316,18 @@ TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) { // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_NE(result->status, EXIT_SUCCESS); + + // unknown policy + // clang-format off + result = ExecuteBinary({"idmap2", + "create", + "--target-apk-path", GetTargetApkPath(), + "--overlay-apk-path", GetOverlayApkPath(), + "--idmap-path", GetIdmapPath(), + "--policy", "this-does-not-exist"}); + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_NE(result->status, EXIT_SUCCESS); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 963f22ec8d72..c6eb71cbeb72 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -184,13 +184,14 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { std::stringstream error; std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); - ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d); + ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xca2093da); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); @@ -216,13 +217,127 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); ASSERT_EQ(types[1]->GetEntryCount(), 4U); - ASSERT_EQ(types[1]->GetEntryOffset(), 3U); + ASSERT_EQ(types[1]->GetEntryOffset(), 8U); ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); ASSERT_EQ(types[1]->GetEntry(3), 0x0002U); } +TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublic) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + "/system-overlay/system-overlay.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 3U); + ASSERT_EQ(types[0]->GetEntryOffset(), 5U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_system_vendor +} + +TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + + "/system-overlay-invalid/system-overlay-invalid.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 5U); + ASSERT_EQ(types[0]->GetEntryOffset(), 3U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_product + ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system_vendor +} + +TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + + "/system-overlay-invalid/system-overlay-invalid.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ false, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 5U); + ASSERT_EQ(types[0]->GetEntryOffset(), 3U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable + ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_product + ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system_vendor +} + TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { std::string target_apk_path(GetTestDataPath()); for (int i = 0; i < 32; i++) { @@ -239,7 +354,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { std::stringstream error; std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, IsNull()); } @@ -255,8 +371,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(overlay_apk, NotNull()); std::stringstream error; - std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + std::unique_ptr<const Idmap> idmap = Idmap::FromApkAssets( + target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp new file mode 100644 index 000000000000..ab567adc6f19 --- /dev/null +++ b/cmds/idmap2/tests/PoliciesTests.cpp @@ -0,0 +1,70 @@ +/* + * 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 <string> + +#include "gtest/gtest.h" + +#include "TestHelpers.h" +#include "idmap2/Policies.h" + +using android::idmap2::PolicyBitmask; +using android::idmap2::PolicyFlags; + +namespace android::idmap2 { + +TEST(PoliciesTests, PoliciesToBitmasks) { + const Result<PolicyBitmask> bitmask1 = PoliciesToBitmask({"system"}, std::cerr); + ASSERT_NE(bitmask1, kResultError); + ASSERT_EQ(bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION); + + const Result<PolicyBitmask> bitmask2 = PoliciesToBitmask({"system", "vendor"}, std::cerr); + ASSERT_NE(bitmask2, kResultError); + ASSERT_EQ(bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); + + const Result<PolicyBitmask> bitmask3 = PoliciesToBitmask({"vendor", "system"}, std::cerr); + ASSERT_NE(bitmask3, kResultError); + ASSERT_EQ(bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); + + const Result<PolicyBitmask> bitmask4 = + PoliciesToBitmask({"public", "product", "system", "vendor"}, std::cerr); + ASSERT_NE(bitmask4, kResultError); + ASSERT_EQ(bitmask4, PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION | + PolicyFlags::POLICY_SYSTEM_PARTITION | + PolicyFlags::POLICY_VENDOR_PARTITION); + + const Result<PolicyBitmask> bitmask5 = + PoliciesToBitmask({"system", "system", "system"}, std::cerr); + ASSERT_NE(bitmask5, kResultError); + ASSERT_EQ(bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION); + + const Result<PolicyBitmask> bitmask6 = PoliciesToBitmask({""}, std::cerr); + ASSERT_EQ(bitmask6, kResultError); + + const Result<PolicyBitmask> bitmask7 = PoliciesToBitmask({"foo"}, std::cerr); + ASSERT_EQ(bitmask7, kResultError); + + const Result<PolicyBitmask> bitmask8 = PoliciesToBitmask({"system", "foo"}, std::cerr); + ASSERT_EQ(bitmask8, kResultError); + + const Result<PolicyBitmask> bitmask9 = PoliciesToBitmask({"system", ""}, std::cerr); + ASSERT_EQ(bitmask9, kResultError); + + const Result<PolicyBitmask> bitmask10 = PoliciesToBitmask({"system "}, std::cerr); + ASSERT_EQ(bitmask10, kResultError); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index 7736bc063131..eaa47cd79533 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -25,6 +25,7 @@ #include "androidfw/Idmap.h" #include "idmap2/Idmap.h" +#include "idmap2/Policies.h" #include "idmap2/PrettyPrintVisitor.h" #include "TestHelpers.h" @@ -32,6 +33,7 @@ using ::testing::NotNull; using android::ApkAssets; +using android::idmap2::PolicyBitmask; namespace android::idmap2 { @@ -46,7 +48,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { std::stringstream error; std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 0318cd20a9bb..b58c61a0c284 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -42,7 +42,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { std::stringstream error; std::unique_ptr<const Idmap> idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; @@ -51,7 +52,7 @@ 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: f5ad1d1d target crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000008: ca2093da target crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000000c: d470336b 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/data/system-overlay-invalid/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml new file mode 100644 index 000000000000..977cd97f21c3 --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="test.overlay.system.invalid"> + <overlay + android:targetPackage="test.target" /> +</manifest> diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build new file mode 100644 index 000000000000..920e1f8ad6f3 --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/build @@ -0,0 +1,26 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar + +aapt2 compile --dir res -o compiled.flata + +aapt2 link \ + --no-resource-removal \ + -I "$FRAMEWORK_RES_APK" \ + --manifest AndroidManifest.xml \ + -o system-overlay-invalid.apk \ + compiled.flata + +rm compiled.flata diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml new file mode 100644 index 000000000000..512770722a4b --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- This overlay will fulfill the policies "public|system". This allows it overlay the + following resources. --> + <string name="policy_system">policy_system</string> + <string name="policy_system_vendor">policy_system_vendor</string> + <string name="policy_public">policy_public</string> + + <!-- It also requests to overlay a resource that belongs to a policy the overlay does not + fulfill.--> + <string name="policy_product">policy_product</string> + + <!-- It also requests to overlay a resource that is not declared as overlayable.--> + <string name="not_overlayable">not_overlayable</string> +</resources> diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk Binary files differnew file mode 100644 index 000000000000..c367f82e21e6 --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk diff --git a/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml new file mode 100644 index 000000000000..8af9064ba64f --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="test.overlay.system"> + <overlay + android:targetPackage="test.target" /> +</manifest> diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build new file mode 100644 index 000000000000..be0d2390f535 --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/build @@ -0,0 +1,26 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar + +aapt2 compile --dir res -o compiled.flata + +aapt2 link \ + --no-resource-removal \ + -I "$FRAMEWORK_RES_APK" \ + --manifest AndroidManifest.xml \ + -o system-overlay.apk \ + compiled.flata + +rm compiled.flata diff --git a/cmds/idmap2/tests/data/system-overlay/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml new file mode 100644 index 000000000000..6aaa0b02639e --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- This overlay will fulfill the policies "public|system". This allows it overlay the + following resources. --> + <string name="policy_system">policy_system</string> + <string name="policy_system_vendor">policy_system_vendor</string> + <string name="policy_public">policy_public</string> +</resources> diff --git a/cmds/idmap2/tests/data/system-overlay/system-overlay.apk b/cmds/idmap2/tests/data/system-overlay/system-overlay.apk Binary files differnew file mode 100644 index 000000000000..90f30eb68c15 --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/system-overlay.apk diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml new file mode 100644 index 000000000000..de19e6fcd022 --- /dev/null +++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> +<overlayable name="TestResources"> + <!-- Publicly overlayable resources --> + <item type="string" name="a" /> + <item type="string" name="b" /> + <item type="string" name="c" /> + <item type="string" name="str1" /> + <item type="string" name="str2" /> + <item type="string" name="str3" /> + <item type="string" name="str4" /> + <item type="string" name="x" /> + <item type="string" name="y" /> + <item type="string" name="z" /> + <item type="integer" name="int1" /> + + <!-- Resources with partition restrictins --> + <policy type="system"> + <item type="string" name="policy_system" /> + </policy> + + <policy type="system|vendor"> + <item type="string" name="policy_system_vendor" /> + </policy> + + <policy type="product"> + <item type="string" name="policy_product" /> + </policy> + + <policy type="public"> + <item type="string" name="policy_public" /> + </policy> +</overlayable> +</resources>
\ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml index 56bf0d60021a..ef9012e49300 100644 --- a/cmds/idmap2/tests/data/target/res/values/values.xml +++ b/cmds/idmap2/tests/data/target/res/values/values.xml @@ -25,4 +25,12 @@ <string name="y">y</string> <string name="z">z</string> <integer name="int1">1</integer> + + <!-- This resources is not marked as overlayable --> + <string name="not_overlayable">not_overlayable</string> + + <string name="policy_system">policy_system</string> + <string name="policy_system_vendor">policy_system_vendor</string> + <string name="policy_product">policy_product</string> + <string name="policy_public">policy_public</string> </resources> diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex 18ecc276caae..9a6220dbe3ca 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 0fa7cffa07af..0a8746a639fc 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -25,6 +25,8 @@ import "frameworks/base/core/proto/android/app/enums.proto"; import "frameworks/base/core/proto/android/app/settings_enums.proto"; import "frameworks/base/core/proto/android/app/job/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/enums.proto"; +import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto"; +import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto"; import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto"; @@ -182,6 +184,10 @@ message Atom { DataStallEvent data_stall_event = 121; RescuePartyResetReported rescue_party_reset_reported = 122; SignedConfigReported signed_config_reported = 123; + GnssNiEventReported gnss_ni_event_reported = 124; + BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125; + BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126; + BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127; } // Pulled events will start at field 10000. @@ -1297,10 +1303,12 @@ message BluetoothEnabledStateChanged { } /** - * Logs when a Bluetooth device connects and disconnects. + * Logs when profiles on a Bluetooth device connects and disconnects. * * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java + * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java + * + * Next Tag: 5 */ message BluetoothConnectionStateChanged { // The state of the connection. @@ -1309,12 +1317,151 @@ message BluetoothConnectionStateChanged { // An identifier that can be used to match connect and disconnect events. // Currently is last two bytes of a hash of a device level ID and // the mac address of the bluetooth device that is connected. - optional int32 obfuscated_id = 2; + // Deprecated: use obfuscated_id instead, this one is always 0 for Q+ + optional int32 OBSOLETE_obfuscated_id = 2 [deprecated = true]; // The profile that is connected. Eg. GATT, A2DP, HEADSET. // From android.bluetooth.BluetoothAdapter.java + // Default: 0 when not used optional int32 bt_profile = 3; + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 4 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Logs when a Bluetooth device connects and disconnects over ACL + * + * Logged from: + * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java + * + * Next Tag: 3 + */ +message BluetoothAclConnectionStateChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // The state of the connection. + // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED. + optional android.bluetooth.ConnectionStateEnum state = 2; +} + +/** + * Logs when a Bluetooth device connects and disconnects over SCO + * + * Logged from: + * packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java + * packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetClientStateMachine.java + * + * Next Tag: 4 + */ +message BluetoothScoConnectionStateChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // The state of the connection. + // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED. + optional android.bluetooth.ConnectionStateEnum state = 2; + // Codec used for this SCO connection + // Default: UNKNOWN + optional android.bluetooth.hfp.ScoCodec codec = 3; +} + +// Logs when there is an event affecting Bluetooth device's link layer connection. +// - This event is triggered when there is a related HCI command or event +// - Users of this metrics can deduce Bluetooth device's connection state from these events +// - HCI commands are logged before the command is sent, after receiving command status, and after +// receiving command complete +// - HCI events are logged when they arrive +// +// Low level log from system/bt +// +// Bluetooth classic commands: +// - CMD_CREATE_CONNECTION +// - CMD_DISCONNECT +// - CMD_CREATE_CONNECTION_CANCEL +// - CMD_ACCEPT_CONNECTION_REQUEST +// - CMD_REJECT_CONNECTION_REQUEST +// - CMD_SETUP_ESCO_CONNECTION +// - CMD_ACCEPT_ESCO_CONNECTION +// - CMD_REJECT_ESCO_CONNECTION +// - CMD_ENH_SETUP_ESCO_CONNECTION +// - CMD_ENH_ACCEPT_ESCO_CONNECTION +// +// Bluetooth low energy commands: +// - CMD_BLE_CREATE_LL_CONN [Only logged on error or when initiator filter policy is 0x00] +// - CMD_BLE_CREATE_CONN_CANCEL [Only logged when there is an error] +// - CMD_BLE_EXTENDED_CREATE_CONNECTION [Only logged on error or when initiator filter policy is 0x00] +// - CMD_BLE_CLEAR_WHITE_LIST +// - CMD_BLE_ADD_WHITE_LIST +// - CMD_BLE_REMOVE_WHITE_LIST +// +// Bluetooth classic events: +// - EVT_CONNECTION_COMP +// - EVT_CONNECTION_REQUEST +// - EVT_DISCONNECTION_COMP +// - EVT_ESCO_CONNECTION_COMP +// - EVT_ESCO_CONNECTION_CHANGED +// +// Bluetooth low energy meta events: +// - BLE_EVT_CONN_COMPLETE_EVT +// - BLE_EVT_ENHANCED_CONN_COMPLETE_EVT +// +// Next tag: 10 +message BluetoothLinkLayerConnectionEvent { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Connection handle of this connection if available + // Range: 0x0000 - 0x0EFF (12 bits) + // Default: 0xFFFF if the handle is unknown + optional int32 connection_handle = 2; + // Direction of the link + // Default: DIRECTION_UNKNOWN + optional android.bluetooth.DirectionEnum direction = 3; + // Type of this link + // Default: LINK_TYPE_UNKNOWN + optional android.bluetooth.LinkTypeEnum type = 4; + + // Reason metadata for this link layer connection event, rules for interpretation: + // 1. If hci_cmd is set and valid, hci_event can be either EVT_COMMAND_STATUS or + // EVT_COMMAND_COMPLETE, ignore hci_ble_event in this case + // 2. If hci_event is set to EVT_BLE_META, look at hci_ble_event; otherwise, if hci_event is + // set and valid, ignore hci_ble_event + + // HCI command associated with this event + // Default: CMD_UNKNOWN + optional android.bluetooth.hci.CommandEnum hci_cmd = 5; + // HCI event associated with this event + // Default: EVT_UNKNOWN + optional android.bluetooth.hci.EventEnum hci_event = 6; + // HCI BLE meta event associated with this event + // Default: BLE_EVT_UNKNOWN + optional android.bluetooth.hci.BleMetaEventEnum hci_ble_event = 7; + // HCI command status code if this is triggerred by hci_cmd + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum cmd_status = 8; + // HCI reason code associated with this event + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum reason_code = 9; } + /** * Logs when something is plugged into or removed from the USB-C connector. * @@ -3184,7 +3331,8 @@ message PackageAssociationSourceProcessStatsProto { optional int32 process_uid = 1; // Process name. optional string process_name = 2; - + // Package name. + optional string package_name = 7; // Total count of the times this association appeared. optional int32 total_count = 3; @@ -3263,6 +3411,9 @@ message ProcessStatsSectionProto { } repeated Status status = 7; + // Number of pages available of various types and sizes, representation fragmentation. + repeated ProcessStatsAvailablePagesProto available_pages = 10; + // Stats for each process. repeated ProcessStatsProto process_stats = 8; @@ -3270,6 +3421,21 @@ message ProcessStatsSectionProto { repeated ProcessStatsPackageProto package_stats = 9; } +message ProcessStatsAvailablePagesProto { + // Node these pages are in (as per /proc/pagetypeinfo) + optional int32 node = 1; + + // Zone these pages are in (as per /proc/pagetypeinfo) + optional string zone = 2; + + // Label for the type of these pages (as per /proc/pagetypeinfo) + optional string label = 3; + + // Distribution of number of pages available by order size. First entry in array is + // order 0, second is order 1, etc. Each order increase is a doubling of page size. + repeated int32 pages_per_order = 4; +} + /** * Pulled from ProcessStatsService.java */ @@ -3902,3 +4068,63 @@ message SignedConfigReported { // Which key was used to verify the config. optional Key verified_with = 5; } + +/* + * Logs GNSS Network-Initiated (NI) location events. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java + */ +message GnssNiEventReported { + // The type of GnssNiEvent. + enum EventType { + UNKNOWN = 0; + NI_REQUEST = 1; + NI_RESPONSE = 2; + } + optional EventType event_type = 1; + + // An ID generated by HAL to associate NI notifications and UI responses. + optional int32 notification_id = 2; + + // A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc. + optional int32 ni_type = 3; + + // NI requires notification. + optional bool need_notify = 4; + + // NI requires verification. + optional bool need_verify = 5; + + // NI requires privacy override, no notification/minimal trace. + optional bool privacy_override = 6; + + // Timeout period to wait for user response. Set to 0 for no timeout limit. Specified in + // seconds. + optional int32 timeout = 7; + + // Default response when timeout. + optional int32 default_response = 8; + + // String representing the requester of the network inititated location request. + optional string requestor_id = 9; + + // Notification message text string representing the service(for eg. SUPL-service) who sent the + // network initiated location request. + optional string text = 10; + + // requestorId decoding scheme. + optional int32 requestor_id_encoding = 11; + + // Notification message text decoding scheme. + optional int32 text_encoding = 12; + + // True if SUPL ES is enabled. + optional bool is_supl_es_enabled = 13; + + // True if GNSS location is enabled. + optional bool is_location_enabled = 14; + + // GNSS NI responses which define the response in NI structures. + optional int32 user_response = 15; +} diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index f995b65cf0db..02eff0b6cee2 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -1108,18 +1108,6 @@ Landroid/os/UserManager;->isUserUnlocked(I)Z Landroid/os/UserManager;->mService:Landroid/os/IUserManager; Landroid/os/UserManager;->removeUser(I)Z Landroid/os/Vibrator;-><init>()V -Landroid/os/VintfObject;->getHalNamesAndVersions()[Ljava/lang/String; -Landroid/os/VintfObject;->getSepolicyVersion()Ljava/lang/String; -Landroid/os/VintfObject;->getTargetFrameworkCompatibilityMatrixVersion()Ljava/lang/Long; -Landroid/os/VintfObject;->getVndkSnapshots()Ljava/util/Map; -Landroid/os/VintfObject;->report()[Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getCpuInfo()Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getHardwareId()Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getKernelVersion()Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getNodeName()Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getOsName()Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getOsRelease()Ljava/lang/String; -Landroid/os/VintfRuntimeInfo;->getOsVersion()Ljava/lang/String; Landroid/os/WorkSource;-><init>(Landroid/os/Parcel;)V Landroid/os/WorkSource;->mNames:[Ljava/lang/String; Landroid/os/WorkSource;->mNum:I @@ -1492,7 +1480,6 @@ Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telepho Landroid/test/AndroidTestCase;->getTestContext()Landroid/content/Context; Landroid/test/AndroidTestCase;->setTestContext(Landroid/content/Context;)V Landroid/test/InstrumentationTestCase;->runMethod(Ljava/lang/reflect/Method;I)V -Landroid/test/RepetitiveTest;->numIterations()I Landroid/util/Singleton;-><init>()V Landroid/util/XmlPullAttributes;-><init>(Lorg/xmlpull/v1/XmlPullParser;)V Landroid/util/XmlPullAttributes;->mParser:Lorg/xmlpull/v1/XmlPullParser; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 98c5a0fb59be..836627efb379 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,7 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.os.Process.myUid; import static java.lang.Character.MIN_VALUE; @@ -123,6 +124,7 @@ import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureSession; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -1025,7 +1027,7 @@ public class Activity extends ContextThemeWrapper */ @Nullable private ContentCaptureManager getContentCaptureManager() { // ContextCapture disabled for system apps - if (getApplicationInfo().isSystemApp()) return null; + if (!UserHandle.isApp(myUid())) return null; if (mContentCaptureManager == null) { mContentCaptureManager = getSystemService(ContentCaptureManager.class); } @@ -1033,13 +1035,15 @@ public class Activity extends ContextThemeWrapper } /** @hide */ private static final int CONTENT_CAPTURE_START = 1; - /** @hide */ private static final int CONTENT_CAPTURE_FLUSH = 2; - /** @hide */ private static final int CONTENT_CAPTURE_STOP = 3; + /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 2; + /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 3; + /** @hide */ private static final int CONTENT_CAPTURE_STOP = 4; /** @hide */ @IntDef(prefix = { "CONTENT_CAPTURE_" }, value = { CONTENT_CAPTURE_START, - CONTENT_CAPTURE_FLUSH, + CONTENT_CAPTURE_PAUSE, + CONTENT_CAPTURE_RESUME, CONTENT_CAPTURE_STOP }) @Retention(RetentionPolicy.SOURCE) @@ -1048,9 +1052,8 @@ public class Activity extends ContextThemeWrapper private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) { final ContentCaptureManager cm = getContentCaptureManager(); - if (cm == null) { - return; - } + if (cm == null) return; + switch (type) { case CONTENT_CAPTURE_START: //TODO(b/111276913): decide whether the InteractionSessionId should be @@ -1062,8 +1065,11 @@ public class Activity extends ContextThemeWrapper } cm.onActivityStarted(mToken, getComponentName(), flags); break; - case CONTENT_CAPTURE_FLUSH: - cm.flush(); + case CONTENT_CAPTURE_PAUSE: + cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED); + break; + case CONTENT_CAPTURE_RESUME: + cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED); break; case CONTENT_CAPTURE_STOP: cm.onActivityStopped(); @@ -1755,7 +1761,7 @@ public class Activity extends ContextThemeWrapper } } mCalled = true; - notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_FLUSH); + notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME); } /** @@ -2149,7 +2155,7 @@ public class Activity extends ContextThemeWrapper } } mCalled = true; - notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_FLUSH); + notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d42326020323..e0b8d78ebabc 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3705,11 +3705,16 @@ public class ActivityManager { * Returns whether switching to provided user was successful. * * @param user the user to switch to. + * + * @throws IllegalArgumentException if the user is null. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) - public boolean switchUser(UserHandle user) { + public boolean switchUser(@NonNull UserHandle user) { + if (user == null) { + throw new IllegalArgumentException("UserHandle cannot be null."); + } return switchUser(user.getIdentifier()); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 1f01e2698fb5..5cac0489e8df 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -320,4 +320,10 @@ public abstract class ActivityManagerInternal { /** Remove pending backup for the given userId. */ public abstract void clearPendingBackup(int userId); + + /** + * When power button is very long pressed, call this interface to do some pre-shutdown work + * like persisting database etc. + */ + public abstract void prepareForPossibleShutdown(); } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 57132a72700d..ab8f234766d6 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -16,6 +16,10 @@ package android.app; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager.StackInfo; @@ -32,7 +36,6 @@ import android.util.Log; import android.view.IWindowManager; import android.view.InputDevice; import android.view.MotionEvent; -import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceSession; @@ -82,7 +85,6 @@ public class ActivityView extends ViewGroup { private boolean mOpened; // Protected by mGuard. private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); - private Surface mTmpSurface = new Surface(); /** The ActivityView is only allowed to contain one task. */ private final boolean mSingleTaskInstance; @@ -319,20 +321,20 @@ public class ActivityView extends ViewGroup { private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { - mTmpSurface = new Surface(); if (mVirtualDisplay == null) { initVirtualDisplay(new SurfaceSession()); if (mVirtualDisplay != null && mActivityViewCallback != null) { mActivityViewCallback.onActivityViewReady(ActivityView.this); } } else { - // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by - // whether it has a surface. Setting a fake surface here so DisplayManager will - // consider this display on. - mVirtualDisplay.setSurface(mTmpSurface); mTmpTransaction.reparent(mRootSurfaceControl, mSurfaceView.getSurfaceControl().getHandle()).apply(); } + + if (mVirtualDisplay != null) { + mVirtualDisplay.setDisplayState(true); + } + updateLocation(); } @@ -346,10 +348,8 @@ public class ActivityView extends ViewGroup { @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { - mTmpSurface.release(); - mTmpSurface = null; if (mVirtualDisplay != null) { - mVirtualDisplay.setSurface(null); + mVirtualDisplay.setDisplayState(false); } cleanTapExcludeRegion(); } @@ -370,15 +370,11 @@ public class ActivityView extends ViewGroup { final int height = mSurfaceView.getHeight(); final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); - // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by - // whether it has a surface. Setting a fake surface here so DisplayManager will consider - // this display on. mVirtualDisplay = displayManager.createVirtualDisplay( - DISPLAY_NAME + "@" + System.identityHashCode(this), - width, height, getBaseDisplayDensity(), mTmpSurface, - DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC - | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY - | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL); + DISPLAY_NAME + "@" + System.identityHashCode(this), width, height, + getBaseDisplayDensity(), null, + VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL); if (mVirtualDisplay == null) { Log.e(TAG, "Failed to initialize ActivityView"); return; @@ -443,11 +439,6 @@ public class ActivityView extends ViewGroup { displayReleased = false; } - if (mTmpSurface != null) { - mTmpSurface.release(); - mTmpSurface = null; - } - if (displayReleased && mActivityViewCallback != null) { mActivityViewCallback.onActivityViewDestroyed(this); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 163be8efc8fd..199c1338c50c 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -65,9 +65,9 @@ interface INotificationManager boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); - void setAppOverlaysAllowed(String pkg, int uid, boolean allowed); - boolean areAppOverlaysAllowed(String pkg); - boolean areAppOverlaysAllowedForPackage(String pkg, int uid); + void setBubblesAllowed(String pkg, int uid, boolean allowed); + boolean areBubblesAllowed(String pkg); + boolean areBubblesAllowedForPackage(String pkg, int uid); void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList); void createNotificationChannels(String pkg, in ParceledListSlice channelsList); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b657a916604b..72819cb1493b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1276,7 +1276,7 @@ public class Notification implements Parcelable private String mShortcutId; private CharSequence mSettingsText; - private PendingIntent mAppOverlayIntent; + private BubbleMetadata mBubbleMetadata; /** @hide */ @IntDef(prefix = { "GROUP_ALERT_" }, value = { @@ -2278,7 +2278,7 @@ public class Notification implements Parcelable mGroupAlertBehavior = parcel.readInt(); if (parcel.readInt() != 0) { - mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel); + mBubbleMetadata = BubbleMetadata.CREATOR.createFromParcel(parcel); } mAllowSystemGeneratedContextualActions = parcel.readBoolean(); @@ -2396,7 +2396,7 @@ public class Notification implements Parcelable that.mBadgeIcon = this.mBadgeIcon; that.mSettingsText = this.mSettingsText; that.mGroupAlertBehavior = this.mGroupAlertBehavior; - that.mAppOverlayIntent = this.mAppOverlayIntent; + that.mBubbleMetadata = this.mBubbleMetadata; that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions; if (!heavy) { @@ -2719,9 +2719,9 @@ public class Notification implements Parcelable parcel.writeInt(mGroupAlertBehavior); - if (mAppOverlayIntent != null) { + if (mBubbleMetadata != null) { parcel.writeInt(1); - mAppOverlayIntent.writeToParcel(parcel, 0); + mBubbleMetadata.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } @@ -3141,11 +3141,11 @@ public class Notification implements Parcelable } /** - * Returns the intent that will be used to display app content in a floating window over the - * existing foreground activity. + * Returns the bubble metadata that will be used to display app content in a floating window + * over the existing foreground activity. */ - public PendingIntent getAppOverlayIntent() { - return mAppOverlayIntent; + public BubbleMetadata getBubbleMetadata() { + return mBubbleMetadata; } /** @@ -3508,19 +3508,18 @@ public class Notification implements Parcelable } /** - * Sets the intent that will be used to display app content in a floating window - * over the existing foreground activity. + * Sets the {@link BubbleMetadata} that will be used to display app content in a floating + * window over the existing foreground activity. * - * <p>This intent will be ignored unless this notification is posted to a channel that - * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p> + * <p>This data will be ignored unless the notification is posted to a channel that + * allows {@link NotificationChannel#canBubble() bubbles}.</p> * - * <p>Notifications with a valid and allowed app overlay intent will be displayed as - * floating windows outside of the notification shade on unlocked devices. When a user - * interacts with one of these windows, this app overlay intent will be invoked and - * displayed.</p> + * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state + * outside of the notification shade on unlocked devices. When a user interacts with the + * collapsed state, the bubble intent will be invoked and displayed.</b> */ - public Builder setAppOverlayIntent(PendingIntent intent) { - mN.mAppOverlayIntent = intent; + public Builder setBubbleMetadata(BubbleMetadata data) { + mN.mBubbleMetadata = data; return this; } @@ -8422,6 +8421,186 @@ public class Notification implements Parcelable } } + /** + * Encapsulates the information needed to display a notification as a bubble. + * + * <p>A bubble is used to display app content in a floating window over the existing + * foreground activity. A bubble has a collapsed state represented by an icon, + * {@link BubbleMetadata.Builder#setIcon(Icon)} and an expanded state which is populated + * via {@link BubbleMetadata.Builder#setIntent(PendingIntent)}.</p> + * + * <b>Notifications with a valid and allowed bubble will display in collapsed state + * outside of the notification shade on unlocked devices. When a user interacts with the + * collapsed bubble, the bubble intent will be invoked and displayed.</b> + * + * @see Notification.Builder#setBubbleMetadata(BubbleMetadata) + */ + public static final class BubbleMetadata implements Parcelable { + + private PendingIntent mPendingIntent; + private CharSequence mTitle; + private Icon mIcon; + private int mDesiredHeight; + + private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) { + mPendingIntent = intent; + mTitle = title; + mIcon = icon; + mDesiredHeight = height; + } + + private BubbleMetadata(Parcel in) { + mPendingIntent = PendingIntent.CREATOR.createFromParcel(in); + mTitle = in.readCharSequence(); + mIcon = Icon.CREATOR.createFromParcel(in); + mDesiredHeight = in.readInt(); + } + + /** + * @return the pending intent used to populate the floating window for this bubble. + */ + public PendingIntent getIntent() { + return mPendingIntent; + } + + /** + * @return the title that will appear along with the app content defined by + * {@link #getIntent()} for this bubble. + */ + public CharSequence getTitle() { + return mTitle; + } + + /** + * @return the icon that will be displayed for this bubble when it is collapsed. + */ + public Icon getIcon() { + return mIcon; + } + + /** + * @return the ideal height for the floating window that app content defined by + * {@link #getIntent()} for this bubble. + */ + public int getDesiredHeight() { + return mDesiredHeight; + } + + public static final Parcelable.Creator<BubbleMetadata> CREATOR = + new Parcelable.Creator<BubbleMetadata>() { + + @Override + public BubbleMetadata createFromParcel(Parcel source) { + return new BubbleMetadata(source); + } + + @Override + public BubbleMetadata[] newArray(int size) { + return new BubbleMetadata[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + mPendingIntent.writeToParcel(out, 0); + out.writeCharSequence(mTitle); + mIcon.writeToParcel(out, 0); + out.writeInt(mDesiredHeight); + } + + /** + * Builder to construct a {@link BubbleMetadata} object. + */ + public static class Builder { + + private PendingIntent mPendingIntent; + private CharSequence mTitle; + private Icon mIcon; + private int mDesiredHeight; + + /** + * Constructs a new builder object. + */ + public Builder() { + } + + /** + * Sets the intent that will be used when the bubble is expanded. This will display the + * app content in a floating window over the existing foreground activity. + */ + public BubbleMetadata.Builder setIntent(PendingIntent intent) { + if (intent == null) { + throw new IllegalArgumentException("Bubble requires non-null pending intent"); + } + mPendingIntent = intent; + return this; + } + + /** + * Sets the title that will appear along with the app content for this bubble. + * + * <p>A title is required and should expect to fit on a single line and make sense when + * shown with the content defined by {@link #setIntent(PendingIntent)}.</p> + */ + public BubbleMetadata.Builder setTitle(CharSequence title) { + if (TextUtils.isEmpty(title)) { + throw new IllegalArgumentException("Bubbles require non-null or empty title"); + } + mTitle = title; + return this; + } + + /** + * Sets the icon that will represent the bubble when it is collapsed. + * + * <p>An icon is required and should be representative of the content within the bubble. + * If your app produces multiple bubbles, the image should be unique for each of them. + * </p> + */ + public BubbleMetadata.Builder setIcon(Icon icon) { + if (icon == null) { + throw new IllegalArgumentException("Bubbles require non-null icon"); + } + mIcon = icon; + return this; + } + + /** + * Sets the desired height for the app content defined by + * {@link #setIntent(PendingIntent)}, this height may not be respected if there is not + * enough space on the screen or if the provided height is too small to be useful. + */ + public BubbleMetadata.Builder setDesiredHeight(int height) { + mDesiredHeight = Math.max(height, 0); + return this; + } + + /** + * Creates the {@link BubbleMetadata} defined by this builder. + * <p>Will throw {@link IllegalStateException} if required fields have not been set + * on this builder.</p> + */ + public BubbleMetadata build() { + if (mPendingIntent == null) { + throw new IllegalStateException("Must supply pending intent to bubble"); + } + if (TextUtils.isEmpty(mTitle)) { + throw new IllegalStateException("Must supply a title for the bubble"); + } + if (mIcon == null) { + throw new IllegalStateException("Must supply an icon for the bubble"); + } + return new BubbleMetadata(mPendingIntent, mTitle, mIcon, mDesiredHeight); + } + } + } + + // When adding a new Style subclass here, don't forget to update // Builder.getNotificationStyleClass. diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 950e9aa939f8..e95d62fc96eb 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_APP_OVERLAY = "app_overlay"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; private static final String DELIMITER = ","; /** @@ -121,7 +121,7 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ - public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100; + public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100; /** * @hide @@ -134,7 +134,7 @@ public final class NotificationChannel implements Parcelable { USER_LOCKED_VIBRATION, USER_LOCKED_SOUND, USER_LOCKED_SHOW_BADGE, - USER_LOCKED_ALLOW_APP_OVERLAY + USER_LOCKED_ALLOW_BUBBLE }; private static final int DEFAULT_LIGHT_COLOR = 0; @@ -144,7 +144,7 @@ public final class NotificationChannel implements Parcelable { NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true; + private static final boolean DEFAULT_ALLOW_BUBBLE = true; @UnsupportedAppUsage private final String mId; @@ -168,7 +168,7 @@ public final class NotificationChannel implements Parcelable { private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; // If this is a blockable system notification channel. private boolean mBlockableSystem = false; - private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY; + private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE; private boolean mImportanceLockedByOEM; /** @@ -231,7 +231,7 @@ public final class NotificationChannel implements Parcelable { mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; mLightColor = in.readInt(); mBlockableSystem = in.readBoolean(); - mAllowAppOverlay = in.readBoolean(); + mAllowBubbles = in.readBoolean(); mImportanceLockedByOEM = in.readBoolean(); } @@ -285,7 +285,7 @@ public final class NotificationChannel implements Parcelable { } dest.writeInt(mLightColor); dest.writeBoolean(mBlockableSystem); - dest.writeBoolean(mAllowAppOverlay); + dest.writeBoolean(mAllowBubbles); dest.writeBoolean(mImportanceLockedByOEM); } @@ -480,7 +480,7 @@ public final class NotificationChannel implements Parcelable { /** * Sets whether notifications posted to this channel can appear outside of the notification - * shade, floating over other apps' content. + * shade, floating over other apps' content as a bubble. * * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is, * channels whose {@link #getImportance() importance} is < @@ -488,10 +488,10 @@ public final class NotificationChannel implements Parcelable { * * <p>Only modifiable before the channel is submitted to * * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p> - * @see Notification#getAppOverlayIntent() + * @see Notification#getBubbleMetadata() */ - public void setAllowAppOverlay(boolean allowAppOverlay) { - mAllowAppOverlay = allowAppOverlay; + public void setAllowBubbles(boolean allowBubbles) { + mAllowBubbles = allowBubbles; } /** @@ -610,16 +610,16 @@ public final class NotificationChannel implements Parcelable { * Returns whether notifications posted to this channel can display outside of the notification * shade, in a floating window on top of other apps. */ - public boolean canOverlayApps() { - return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH; + public boolean canBubble() { + return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH; } /** - * Like {@link #canOverlayApps()}, but only checks the permission, not the importance. + * Like {@link #canBubble()}, but only checks the permission, not the importance. * @hide */ - public boolean isAppOverlayAllowed() { - return mAllowAppOverlay; + public boolean isBubbleAllowed() { + return mAllowBubbles; } /** @@ -719,7 +719,7 @@ public final class NotificationChannel implements Parcelable { lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); - setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); + setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); } @Nullable @@ -838,8 +838,8 @@ public final class NotificationChannel implements Parcelable { if (isBlockableSystem()) { out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem())); } - if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) { - out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps())); + if (canBubble() != DEFAULT_ALLOW_BUBBLE) { + out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble())); } out.endTag(null, TAG_CHANNEL); @@ -883,7 +883,7 @@ public final class NotificationChannel implements Parcelable { record.put(ATT_DELETED, Boolean.toString(isDeleted())); record.put(ATT_GROUP, getGroup()); record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem()); - record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps()); + record.put(ATT_ALLOW_BUBBLE, canBubble()); return record; } @@ -983,7 +983,7 @@ public final class NotificationChannel implements Parcelable { && mShowBadge == that.mShowBadge && isDeleted() == that.isDeleted() && isBlockableSystem() == that.isBlockableSystem() - && mAllowAppOverlay == that.mAllowAppOverlay + && mAllowBubbles == that.mAllowBubbles && Objects.equals(getId(), that.getId()) && Objects.equals(getName(), that.getName()) && Objects.equals(mDesc, that.mDesc) @@ -1000,7 +1000,7 @@ public final class NotificationChannel implements Parcelable { getLockscreenVisibility(), getSound(), mLights, getLightColor(), getUserLockedFields(), isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(), - getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay, + getAudioAttributes(), isBlockableSystem(), mAllowBubbles, mImportanceLockedByOEM); result = 31 * result + Arrays.hashCode(mVibration); return result; @@ -1028,7 +1028,7 @@ public final class NotificationChannel implements Parcelable { + ", mGroup='" + mGroup + '\'' + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem - + ", mAllowAppOverlay=" + mAllowAppOverlay + + ", mAllowBubbles=" + mAllowBubbles + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + '}'; pw.println(prefix + output); @@ -1055,7 +1055,7 @@ public final class NotificationChannel implements Parcelable { + ", mGroup='" + mGroup + '\'' + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem - + ", mAllowAppOverlay=" + mAllowAppOverlay + + ", mAllowBubbles=" + mAllowBubbles + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + '}'; } @@ -1090,7 +1090,7 @@ public final class NotificationChannel implements Parcelable { mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES); } proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem); - proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay); + proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles); proto.end(token); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index aad32532bbe8..43614feb28a4 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1080,14 +1080,14 @@ public class NotificationManager { * notification shade, floating over other apps' content. * * <p>This value will be ignored for notifications that are posted to channels that do not - * allow app overlays ({@link NotificationChannel#canOverlayApps()}. + * allow bubbles ({@link NotificationChannel#canBubble()}. * - * @see Notification#getAppOverlayIntent() + * @see Notification#getBubbleMetadata() */ - public boolean areAppOverlaysAllowed() { + public boolean areBubblesAllowed() { INotificationManager service = getService(); try { - return service.areAppOverlaysAllowed(mContext.getPackageName()); + return service.areBubblesAllowed(mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9247486dff40..6d7c547f7cf1 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2168,6 +2168,74 @@ public class DevicePolicyManager { public @interface SetPrivateDnsModeResultConstants {} /** + * Activity action: Starts the administrator to get the mode for the provisioning. + * This intent may contain the following extras: + * <ul> + * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</li> + * <li>{@link #EXTRA_PROVISIONING_IMEI}</li> + * <li>{@link #EXTRA_PROVISIONING_SERIAL_NUMBER}</li> + * </ul> + * + * <p>The target activity should return one of the following values in + * {@link #EXTRA_PROVISIONING_MODE} as result: + * <ul> + * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE}</li> + * </ul> + * + * <p>The target activity may also return the account that needs to be migrated from primary + * user to managed profile in case of a profile owner provisioning in + * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result. + */ + public static final String ACTION_GET_PROVISIONING_MODE = + "android.app.action.GET_PROVISIONING_MODE"; + + /** + * A string extra holding the IMEI (International Mobile Equipment Identity) of the device. + */ + public static final String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI"; + + /** + * A string extra holding the serial number of the device. + */ + public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = + "android.app.extra.PROVISIONING_SERIAL_NUMBER"; + + /** + * An intent extra holding the provisioning mode returned by the administrator. + * The value for this extra should be one of the following: + * <ul> + * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE}</li> + * </ul> + */ + public static final String EXTRA_PROVISIONING_MODE = + "android.app.extra.PROVISIONING_MODE"; + + /** + * The provisioning mode for fully managed device. + */ + public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; + + /** + * The provisioning mode for managed profile. + */ + public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; + + /** + * The provisioning mode for managed profile on a fully managed device. + */ + public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 3; + + /** + * Activity action: Starts the administrator to show policy compliance for the provisioning. + */ + public static final String ACTION_ADMIN_POLICY_COMPLIANCE = + "android.app.action.ADMIN_POLICY_COMPLIANCE"; + + /** * Return true if the given administrator component is currently active (enabled) in the system. * * @param admin The administrator component to check for. diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java index a79ad2fc8607..aaae57e526a0 100644 --- a/core/java/android/app/usage/EventList.java +++ b/core/java/android/app/usage/EventList.java @@ -103,21 +103,4 @@ public class EventList { } return result; } - - /** - * Remove events of certain type on or after a timestamp. - * @param type The type of event to remove. - * @param timeStamp the timeStamp on or after which to remove the event. - */ - public void removeOnOrAfter(int type, long timeStamp) { - for (int i = mEvents.size() - 1; i >= 0; i--) { - UsageEvents.Event event = mEvents.get(i); - if (event.mTimeStamp < timeStamp) { - break; - } - if (event.mEventType == type) { - mEvents.remove(i); - } - } - } } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index d7a53281bc56..2c5fe046faad 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -245,10 +245,18 @@ public final class UsageEvents implements Parcelable { public static final int FLUSH_TO_DISK = 25; /** + * An event type denoting that the device underwent a shutdown process. + * A DEVICE_SHUTDOWN event should be treated as if all started activities and foreground + * services are now stopped and no explicit {@link #ACTIVITY_STOPPED} and + * {@link #FOREGROUND_SERVICE_STOP} events will be generated for them. + */ + public static final int DEVICE_SHUTDOWN = 26; + + /** * Keep in sync with the greatest event type value. * @hide */ - public static final int MAX_EVENT_TYPE = 25; + public static final int MAX_EVENT_TYPE = 26; /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 308180badbb8..94a2a3eaae7f 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; @@ -119,12 +120,9 @@ public final class UsageStats implements Parcelable { public int mLastEvent; /** - * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state), - * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()), - * it is removed from this map. * Key is instanceId of the activity (ActivityRecode appToken hashCode).. - * Value is this activity's last event, one of ACTIVITY_RESUMED or - * ACTIVITY_PAUSED. + * Value is this activity's last event, one of ACTIVITY_RESUMED, ACTIVITY_PAUSED or + * ACTIVITY_STOPPED. * {@hide} */ public SparseIntArray mActivities = new SparseIntArray(); @@ -560,6 +558,7 @@ public final class UsageStats implements Parcelable { mLastTimeForegroundServiceUsed = timeStamp; mForegroundServices.put(className, eventType); break; + case DEVICE_SHUTDOWN: case FLUSH_TO_DISK: // update usage of all active activities/services. if (hasForegroundActivity()) { diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 2edad350e18e..cc3ab0025864 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -98,6 +98,12 @@ public abstract class UsageStatsManagerInternal { public abstract void prepareShutdown(); /** + * When the device power button is long pressed for 3.5 seconds, prepareForPossibleShutdown() + * is called. + */ + public abstract void prepareForPossibleShutdown(); + + /** * Returns true if the app has not been used for a certain amount of time. How much time? * Could be hours, could be days, who knows? * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 636a70f040da..b84567383bfb 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5555,26 +5555,24 @@ public abstract class PackageManager { } /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void addPackageToPreferred(String packageName); /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void removePackageFromPreferred(String packageName); /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. - * * Retrieve the list of all currently configured preferred packages. The * first package on the list is the most preferred, the last is the least * preferred. @@ -5582,15 +5580,16 @@ public abstract class PackageManager { * @param flags Additional option flags to modify the data returned. * @return A List of PackageInfo objects, one for each preferred * application, in order of preference. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags); /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Add a new preferred activity mapping to the system. This will be used * to automatically select the given activity component when * {@link Context#startActivity(Intent) Context.startActivity()} finds @@ -5604,20 +5603,26 @@ public abstract class PackageManager { * this preference was made. * @param activity The component name of the activity that is to be * preferred. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity); /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Same as {@link #addPreferredActivity(IntentFilter, int, ComponentName[], ComponentName)}, but with a specific userId to apply the preference to. * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @UnsupportedAppUsage @@ -5627,10 +5632,6 @@ public abstract class PackageManager { } /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Replaces an existing preferred activity mapping to the system, and if that were not present * adds a new preferred activity. This will be used * to automatically select the given activity component when @@ -5645,7 +5646,13 @@ public abstract class PackageManager { * this preference was made. * @param activity The component name of the activity that is to be * preferred. + * * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @UnsupportedAppUsage @@ -5653,10 +5660,6 @@ public abstract class PackageManager { ComponentName[] set, ComponentName activity); /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Replaces an existing preferred activity mapping to the system, and if that were not present * adds a new preferred activity. This will be used to automatically select the given activity * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple @@ -5671,6 +5674,11 @@ public abstract class PackageManager { * @param activity The component name of the activity that is to be preferred. * * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @SystemApi @@ -5681,6 +5689,11 @@ public abstract class PackageManager { /** * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @UnsupportedAppUsage @@ -5690,10 +5703,6 @@ public abstract class PackageManager { } /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. - * * Remove all preferred activity mappings, previously added with * {@link #addPreferredActivity}, from the * system whose activities are implemented in the given package name. @@ -5701,15 +5710,16 @@ public abstract class PackageManager { * * @param packageName The name of the package whose preferred activity * mappings are to be removed. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void clearPackagePreferredActivities(String packageName); /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. - * * Retrieve all preferred activities, previously added with * {@link #addPreferredActivity}, that are * currently registered with the system. @@ -5725,6 +5735,11 @@ public abstract class PackageManager { * @return Returns the total number of registered preferred activities * (the number of distinct IntentFilter records, not the number of unique * activity components) that were found. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters, diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 83563d0fd9f6..359adbdb6e24 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -55,6 +55,7 @@ public abstract class PackageManagerInternal { public static final int PACKAGE_PERMISSION_CONTROLLER = 6; public static final int PACKAGE_WELLBEING = 7; public static final int PACKAGE_DOCUMENTER = 8; + public static final int PACKAGE_CONFIGURATOR = 9; @IntDef(value = { PACKAGE_SYSTEM, PACKAGE_SETUP_WIZARD, @@ -65,6 +66,7 @@ public abstract class PackageManagerInternal { PACKAGE_PERMISSION_CONTROLLER, PACKAGE_WELLBEING, PACKAGE_DOCUMENTER, + PACKAGE_CONFIGURATOR, }) @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage {} @@ -137,6 +139,12 @@ public abstract class PackageManagerInternal { public abstract void setLocationPackagesProvider(PackagesProvider provider); /** + * Set the location extra packages provider. + * @param provider The packages provider. + */ + public abstract void setLocationExtraPackagesProvider(PackagesProvider provider); + + /** * Sets the voice interaction packages provider. * @param provider The packages provider. */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0fc50c6911b5..dbf3574aebee 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2656,6 +2656,23 @@ public class PackageParser { } /** + * Matches a given {@code targetCode} against a set of release codeNames. Target codes can + * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form + * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}). + */ + private static boolean matchTargetCode(@NonNull String[] codeNames, + @NonNull String targetCode) { + final String targetCodeName; + final int targetCodeIdx = targetCode.indexOf('.'); + if (targetCodeIdx == -1) { + targetCodeName = targetCode; + } else { + targetCodeName = targetCode.substring(0, targetCodeIdx); + } + return ArrayUtils.contains(codeNames, targetCodeName); + } + + /** * Computes the targetSdkVersion to use at runtime. If the package is not * compatible with this platform, populates {@code outError[0]} with an * error message. @@ -2698,7 +2715,7 @@ public class PackageParser { // If it's a pre-release SDK and the codename matches this platform, it // definitely targets this SDK. - if (ArrayUtils.contains(platformSdkCodenames, targetCode) || forceCurrentDev) { + if (matchTargetCode(platformSdkCodenames, targetCode) || forceCurrentDev) { return Build.VERSION_CODES.CUR_DEVELOPMENT; } @@ -2831,7 +2848,7 @@ public class PackageParser { // If it's a pre-release SDK and the codename matches this platform, we // definitely meet the minimum SDK requirement. - if (ArrayUtils.contains(platformSdkCodenames, minCode)) { + if (matchTargetCode(platformSdkCodenames, minCode)) { return Build.VERSION_CODES.CUR_DEVELOPMENT; } @@ -3760,9 +3777,11 @@ public class PackageParser { ai.flags |= ApplicationInfo.FLAG_MULTIARCH; } + final boolean extractNativeLibsDefault = + owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q; if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs, - true)) { + extractNativeLibsDefault)) { ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS; } diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index a3395ac9fd13..5d2cf0a7d101 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -203,6 +203,16 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @TestApi public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000; + /** + * Additional flag for {@link #protectionLevel}, corresponding to the + * {@code configurator} value of {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + @TestApi + public static final int PROTECTION_FLAG_CONFIGURATOR = 0x80000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { @@ -222,6 +232,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER, PROTECTION_FLAG_WELLBEING, PROTECTION_FLAG_DOCUMENTER, + PROTECTION_FLAG_CONFIGURATOR, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -417,6 +428,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) { protLevel += "|documenter"; } + if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) { + protLevel += "|configurator"; + } return protLevel; } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index ec2e2fd474a2..fe68b8a048c2 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.UserIdInt; +import android.app.Person; import android.app.TaskStackBuilder; import android.content.ComponentName; import android.content.Context; @@ -111,6 +112,9 @@ public final class ShortcutInfo implements Parcelable { public static final int FLAG_SHADOW = 1 << 12; /** @hide */ + public static final int FLAG_LONG_LIVED = 1 << 13; + + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_DYNAMIC, FLAG_PINNED, @@ -124,6 +128,8 @@ public final class ShortcutInfo implements Parcelable { FLAG_ADAPTIVE_BITMAP, FLAG_RETURNED_BY_SERVICE, FLAG_ICON_FILE_PENDING_SAVE, + FLAG_SHADOW, + FLAG_LONG_LIVED, }) @Retention(RetentionPolicy.SOURCE) public @interface ShortcutFlags {} @@ -344,6 +350,9 @@ public final class ShortcutInfo implements Parcelable { @Nullable private PersistableBundle[] mIntentPersistableExtrases; + @Nullable + private Person[] mPersons; + private int mRank; /** @@ -399,6 +408,10 @@ public final class ShortcutInfo implements Parcelable { mCategories = cloneCategories(b.mCategories); mIntents = cloneIntents(b.mIntents); fixUpIntentExtras(); + mPersons = clonePersons(b.mPersons); + if (b.mIsLongLived) { + setLongLived(); + } mRank = b.mRank; mExtras = b.mExtras; updateTimestamp(); @@ -465,6 +478,20 @@ public final class ShortcutInfo implements Parcelable { return ret; } + private static Person[] clonePersons(Person[] persons) { + if (persons == null) { + return null; + } + final Person[] ret = new Person[persons.length]; + for (int i = 0; i < ret.length; i++) { + if (persons[i] != null) { + // Don't need to keep the icon, remove it to save space + ret[i] = persons[i].toBuilder().setIcon(null).build(); + } + } + return ret; + } + /** * Throws if any of the mandatory fields is not set. * @@ -511,6 +538,7 @@ public final class ShortcutInfo implements Parcelable { mDisabledMessage = source.mDisabledMessage; mDisabledMessageResId = source.mDisabledMessageResId; mCategories = cloneCategories(source.mCategories); + mPersons = clonePersons(source.mPersons); if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) { mIntents = cloneIntents(source.mIntents); mIntentPersistableExtrases = @@ -833,6 +861,9 @@ public final class ShortcutInfo implements Parcelable { if (source.mCategories != null) { mCategories = cloneCategories(source.mCategories); } + if (source.mPersons != null) { + mPersons = clonePersons(source.mPersons); + } if (source.mIntents != null) { mIntents = cloneIntents(source.mIntents); mIntentPersistableExtrases = @@ -901,6 +932,10 @@ public final class ShortcutInfo implements Parcelable { private Intent[] mIntents; + private Person[] mPersons; + + private boolean mIsLongLived; + private int mRank = RANK_NOT_SET; private PersistableBundle mExtras; @@ -1165,6 +1200,53 @@ public final class ShortcutInfo implements Parcelable { } /** + * Add a person that is relevant to this shortcut. Alternatively, + * {@link #setPersons(Person[])} can be used to add multiple persons to a shortcut. + * + * <p> This is an optional field, but the addition of person may cause this shortcut to + * appear more prominently in the user interface (e.g. ShareSheet). + * + * <p> A person should usually contain a uri in order to benefit from the ranking boost. + * However, even if no uri is provided, it's beneficial to provide people in the shortcut, + * such that listeners and voice only devices can announce and handle them properly. + * + * @see Person + * @see #setPersons(Person[]) + */ + @NonNull + public Builder setPerson(@NonNull Person person) { + return setPersons(new Person[]{person}); + } + + /** + * Sets multiple persons instead of a single person. + * + * @see Person + * @see #setPerson(Person) + */ + @NonNull + public Builder setPersons(@NonNull Person[] persons) { + Preconditions.checkNotNull(persons, "persons cannot be null"); + Preconditions.checkNotNull(persons.length, "persons cannot be empty"); + for (Person person : persons) { + Preconditions.checkNotNull(person, "persons cannot contain null"); + } + mPersons = clonePersons(persons); + return this; + } + + /** + * Sets if a shortcut would be valid even if it has been unpublished/invisible by the app + * (as a dynamic or pinned shortcut). If it is long lived, it can be cached by various + * system services even after it has been unpublished as a dynamic shortcut. + */ + @NonNull + public Builder setLongLived() { + mIsLongLived = true; + return this; + } + + /** * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app * to sort shortcuts. * @@ -1395,6 +1477,16 @@ public final class ShortcutInfo implements Parcelable { } /** + * Return the Persons set with {@link Builder#setPersons(Person[])}. + * + * @hide + */ + @Nullable + public Person[] getPersons() { + return clonePersons(mPersons); + } + + /** * The extras in the intents. We convert extras into {@link PersistableBundle} so we can * persist them. * @hide @@ -1525,6 +1617,16 @@ public final class ShortcutInfo implements Parcelable { addFlags(FLAG_RETURNED_BY_SERVICE); } + /** @hide */ + public boolean isLongLived() { + return hasFlags(FLAG_LONG_LIVED); + } + + /** @hide */ + public void setLongLived() { + addFlags(FLAG_LONG_LIVED); + } + /** Return whether a shortcut is dynamic. */ public boolean isDynamic() { return hasFlags(FLAG_DYNAMIC); @@ -1893,6 +1995,8 @@ public final class ShortcutInfo implements Parcelable { mCategories.add(source.readString().intern()); } } + + mPersons = source.readParcelableArray(cl, Person.class); } @Override @@ -1940,6 +2044,8 @@ public final class ShortcutInfo implements Parcelable { } else { dest.writeInt(0); } + + dest.writeParcelableArray(mPersons, flags); } public static final Creator<ShortcutInfo> CREATOR = @@ -2040,6 +2146,9 @@ public final class ShortcutInfo implements Parcelable { if (isReturnedByServer()) { sb.append("Rets"); } + if (isLongLived()) { + sb.append("Liv"); + } sb.append("]"); addIndentOrComma(sb, indent); @@ -2094,6 +2203,11 @@ public final class ShortcutInfo implements Parcelable { addIndentOrComma(sb, indent); + sb.append("persons="); + sb.append(mPersons); + + addIndentOrComma(sb, indent); + sb.append("icon="); sb.append(mIcon); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index c4b315ec90c8..dcd6e2ed94c7 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -98,6 +98,12 @@ import java.util.ArrayList; * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p> */ public class Resources { + /** + * The {@code null} resource ID. This denotes an invalid resource ID that is returned by the + * system when a resource is not found or the value is set to {@code @null} in XML. + */ + public static final @AnyRes int ID_NULL = 0; + static final String TAG = "Resources"; private static final Object sSync = new Object(); diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 508626bdd869..d53834c657cc 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -19,6 +19,7 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ColorInt; import android.annotation.Nullable; +import android.annotation.StyleRes; import android.annotation.StyleableRes; import android.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; @@ -63,13 +64,15 @@ public class TypedArray { } // STYLE_ prefixed constants are offsets within the typed data array. - static final int STYLE_NUM_ENTRIES = 6; + // Keep this in sync with libs/androidfw/include/androidfw/AttributeResolution.h + static final int STYLE_NUM_ENTRIES = 7; static final int STYLE_TYPE = 0; static final int STYLE_DATA = 1; static final int STYLE_ASSET_COOKIE = 2; static final int STYLE_RESOURCE_ID = 3; static final int STYLE_CHANGING_CONFIGURATIONS = 4; static final int STYLE_DENSITY = 5; + static final int SYTLE_SOURCE_STYLE_RESOURCE_ID = 6; @UnsupportedAppUsage private final Resources mResources; @@ -1098,6 +1101,31 @@ public class TypedArray { } /** + * Returns the resource ID of the style against which the specified attribute was resolved, + * otherwise returns defValue. + * + * @param index Index of attribute whose source style to retrieve. + * @param defValue Value to return if the attribute is not defined or + * not a resource. + * + * @return Attribute source style resource ID or defValue if it was not resolved in any style. + * @throws RuntimeException if the TypedArray has already been recycled. + */ + @StyleRes + public int getSourceStyleResourceId(@StyleableRes int index, @StyleRes int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + + index *= STYLE_NUM_ENTRIES; + final int resid = mData[index + SYTLE_SOURCE_STYLE_RESOURCE_ID]; + if (resid != 0) { + return resid; + } + return defValue; + } + + /** * Determines whether there is an attribute at <var>index</var>. * <p> * <strong>Note:</strong> If the attribute was set to {@code @empty} or @@ -1309,6 +1337,7 @@ public class TypedArray { data[index + STYLE_CHANGING_CONFIGURATIONS]); outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; + outValue.sourceStyleResourceId = data[index + SYTLE_SOURCE_STYLE_RESOURCE_ID]; return true; } diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index eb3414d2ca48..8aac1bfa1f8d 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -42,13 +42,13 @@ public class BiometricManager { /** * The hardware is unavailable. Try again later. */ - public static final int BIOMETRIC_ERROR_UNAVAILABLE = + public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE; /** * The user does not have any biometrics enrolled. */ - public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = + public static final int BIOMETRIC_ERROR_NONE_ENROLLED = BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS; /** @@ -58,8 +58,8 @@ public class BiometricManager { BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT; @IntDef({BIOMETRIC_SUCCESS, - BIOMETRIC_ERROR_UNAVAILABLE, - BIOMETRIC_ERROR_NO_BIOMETRICS, + BIOMETRIC_ERROR_HW_UNAVAILABLE, + BIOMETRIC_ERROR_NONE_ENROLLED, BIOMETRIC_ERROR_NO_HARDWARE}) @interface BiometricError {} @@ -95,8 +95,8 @@ public class BiometricManager { * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt} * can be expected to be shown (hardware available, templates enrolled, user-enabled). * - * @return Returns {@link #BIOMETRIC_ERROR_NO_BIOMETRICS} if the user does not have any - * enrolled, or {@link #BIOMETRIC_ERROR_UNAVAILABLE} if none are currently + * @return Returns {@link #BIOMETRIC_ERROR_NONE_ENROLLED} if the user does not have any + * enrolled, or {@link #BIOMETRIC_ERROR_HW_UNAVAILABLE} if none are currently * supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if a biometric can currently be * used (enrolled and available). */ @@ -113,7 +113,7 @@ public class BiometricManager { return BIOMETRIC_ERROR_NO_HARDWARE; } else { Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected"); - return BIOMETRIC_ERROR_UNAVAILABLE; + return BIOMETRIC_ERROR_HW_UNAVAILABLE; } } } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index f652f85c153b..c69b68e4360a 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -235,7 +235,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * requiring confirmation. * * @param requireConfirmation - * @hide */ public Builder setRequireConfirmation(boolean requireConfirmation) { mBundle.putBoolean(KEY_REQUIRE_CONFIRMATION, requireConfirmation); diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index be054297c769..8c74ddc7698c 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -91,9 +91,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @return The matching brightness correction, or null. * - * @hide */ - @SystemApi @Nullable public BrightnessCorrection getCorrectionByPackageName(String packageName) { return mCorrectionsByPackageName.get(packageName); @@ -106,10 +104,7 @@ public final class BrightnessConfiguration implements Parcelable { * The app category. * * @return The matching brightness correction, or null. - * - * @hide */ - @SystemApi @Nullable public BrightnessCorrection getCorrectionByCategory(int category) { return mCorrectionsByCategory.get(category); @@ -416,9 +411,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @return The maximum number of corrections by package name allowed. * - * @hide */ - @SystemApi public int getMaxCorrectionsByPackageName() { return MAX_CORRECTIONS_BY_PACKAGE_NAME; } @@ -428,9 +421,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @return The maximum number of corrections by category allowed. * - * @hide */ - @SystemApi public int getMaxCorrectionsByCategory() { return MAX_CORRECTIONS_BY_CATEGORY; } @@ -451,9 +442,7 @@ public final class BrightnessConfiguration implements Parcelable { * Maximum number of corrections by package name exceeded (see * {@link #getMaxCorrectionsByPackageName}). * - * @hide */ - @SystemApi public Builder addCorrectionByPackageName(String packageName, BrightnessCorrection correction) { if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) { @@ -479,9 +468,7 @@ public final class BrightnessConfiguration implements Parcelable { * Maximum number of corrections by category exceeded (see * {@link #getMaxCorrectionsByCategory}). * - * @hide */ - @SystemApi public Builder addCorrectionByCategory(@ApplicationInfo.Category int category, BrightnessCorrection correction) { if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) { @@ -504,8 +491,6 @@ public final class BrightnessConfiguration implements Parcelable { /** * Builds the {@link BrightnessConfiguration}. - * - * A brightness curve <b>must</b> be set before calling this. */ public BrightnessConfiguration build() { if (mCurveLux == null || mCurveNits == null) { diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java index c4e0e3b723cd..6a073ffaaa5b 100644 --- a/core/java/android/hardware/display/BrightnessCorrection.java +++ b/core/java/android/hardware/display/BrightnessCorrection.java @@ -18,6 +18,7 @@ package android.hardware.display; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.MathUtils; @@ -41,6 +42,7 @@ import java.io.IOException; * @hide */ @SystemApi +@TestApi public final class BrightnessCorrection implements Parcelable { private static final int SCALE_AND_TRANSLATE_LOG = 1; @@ -98,6 +100,24 @@ public final class BrightnessCorrection implements Parcelable { return mImplementation.toString(); } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof BrightnessCorrection)) { + return false; + } + BrightnessCorrection other = (BrightnessCorrection) o; + return other.mImplementation.equals(mImplementation); + } + + @Override + public int hashCode() { + return mImplementation.hashCode(); + } + public static final Creator<BrightnessCorrection> CREATOR = new Creator<BrightnessCorrection>() { public BrightnessCorrection createFromParcel(Parcel in) { @@ -215,6 +235,26 @@ public final class BrightnessCorrection implements Parcelable { } @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScaleAndTranslateLog)) { + return false; + } + ScaleAndTranslateLog other = (ScaleAndTranslateLog) o; + return other.mScale == mScale && other.mTranslate == mTranslate; + } + + @Override + public int hashCode() { + int result = 1; + result = result * 31 + Float.hashCode(mScale); + result = result * 31 + Float.hashCode(mTranslate); + return result; + } + + @Override public void writeToParcel(Parcel dest) { dest.writeInt(SCALE_AND_TRANSLATE_LOG); dest.writeFloat(mScale); diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index cda8498c4f01..7e45441c804a 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -436,6 +436,7 @@ public final class DisplayManagerGlobal { public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { try { mDm.setVirtualDisplaySurface(token, surface); + setVirtualDisplayState(token, surface != null); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -458,6 +459,14 @@ public final class DisplayManagerGlobal { } } + void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) { + try { + mDm.setVirtualDisplayState(token, isOn); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + /** * Gets the stable device display size, in pixels. */ diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 2d81cdfec179..aae8afbcad49 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -82,6 +82,9 @@ interface IDisplayManager { // No permissions required but must be same Uid as the creator. void releaseVirtualDisplay(in IVirtualDisplayCallback token); + // No permissions required but must be same Uid as the creator. + void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn); + // Get a stable metric for the device's display size. No permissions required. Point getStableDisplaySize(); diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java index d35466605162..bf62c95cf6db 100644 --- a/core/java/android/hardware/display/VirtualDisplay.java +++ b/core/java/android/hardware/display/VirtualDisplay.java @@ -104,6 +104,18 @@ public final class VirtualDisplay { } } + /** + * Sets the on/off state for a virtual display. + * + * @param isOn Whether the display should be on or off. + * @hide + */ + public void setDisplayState(boolean isOn) { + if (mToken != null) { + mGlobal.setVirtualDisplayState(mToken, isOn); + } + } + @Override public String toString() { return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java index d382eb9310d9..bdd5ab67af48 100644 --- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java +++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java @@ -15,10 +15,12 @@ */ package android.hardware.hdmi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; @@ -56,6 +58,21 @@ public final class HdmiAudioSystemClient extends HdmiClient { mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler; } + /** + * Callback interface used to get the set System Audio Mode result. + * + * @hide + */ + // TODO(b/110094868): unhide and add @SystemApi for Q + public interface SetSystemAudioModeCallback { + /** + * Called when the input was changed. + * + * @param result the result of the set System Audio Mode + */ + void onComplete(int result); + } + /** @hide */ // TODO(b/110094868): unhide and add @SystemApi for Q @Override @@ -117,4 +134,34 @@ public final class HdmiAudioSystemClient extends HdmiClient { mPendingReportAudioStatus = true; } } + + /** + * Set System Audio Mode on/off with audio system device. + * + * @param state true to set System Audio Mode on. False to set off. + * @param callback callback offer the setting result. + * + * @hide + */ + // TODO(b/110094868): unhide and add @SystemApi for Q + public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) { + // TODO(amyjojo): implement this when needed. + } + + /** + * When device is switching to an audio only source, this method is called to broadcast + * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or + * TV supporting System Audio Control or not. This is to get volume control passthrough + * from STB even if TV does not support it. + * + * @hide + */ + // TODO(b/110094868): unhide and add @SystemApi for Q + public void setSystemAudioModeOnForAudioOnlySource() { + try { + mService.setSystemAudioModeOnForAudioOnlySource(); + } catch (RemoteException e) { + Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source"); + } + } } diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index f5d288e6a233..a98b31ad6a5e 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -33,6 +33,8 @@ import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; +import java.util.List; + /** * The {@link HdmiControlManager} class is used to send HDMI control messages * to attached CEC devices. @@ -53,6 +55,10 @@ public final class HdmiControlManager { @Nullable private final IHdmiControlService mService; + private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; + + private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; + /** * Broadcast Action: Display OSD message. * <p>Send when the service has a message to display on screen for events @@ -404,6 +410,72 @@ public final class HdmiControlManager { } /** + * Get a snapshot of the real-time status of the remote devices. + * + * @return a list of {@link HdmiDeviceInfo} of the devices connected to the current device. + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public List<HdmiDeviceInfo> getConnectedDevicesList() { + try { + return mService.getDeviceList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Power off the target device. + * + * @param deviceInfo HdmiDeviceInfo of the device to be powered off + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public void powerOffRemoteDevice(HdmiDeviceInfo deviceInfo) { + try { + mService.powerOffRemoteDevice( + deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Power on the target device. + * + * @param deviceInfo HdmiDeviceInfo of the device to be powered on + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) { + try { + mService.powerOnRemoteDevice( + deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Ask the target device to be the new Active Source. + * + * @param deviceInfo HdmiDeviceInfo of the target device + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public void askRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) { + try { + mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Controls standby mode of the system. It will also try to turn on/off the connected devices if * necessary. * @@ -432,6 +504,46 @@ public final class HdmiControlManager { } /** + * Get the physical address of the device. + * + * @hide + */ + public int getPhysicalAddress() { + if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) { + return mPhysicalAddress; + } + try { + mPhysicalAddress = mService.getPhysicalAddress(); + return mPhysicalAddress; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check if the target device is connected to the current device. The + * API also returns true if the current device is the target. + * + * @param targetDevice {@link HdmiDeviceInfo} of the target device. + * @return true if {@code device} is directly or indirectly connected to the + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public boolean isTargetDeviceConnected(HdmiDeviceInfo targetDevice) { + mPhysicalAddress = getPhysicalAddress(); + if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { + return false; + } + int targetPhysicalAddress = targetDevice.getPhysicalAddress(); + if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { + return false; + } + return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress) + != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + /** * Listener used to get hotplug event from HDMI port. */ public interface HotplugEventListener { diff --git a/core/java/android/hardware/hdmi/HdmiUtils.java b/core/java/android/hardware/hdmi/HdmiUtils.java new file mode 100644 index 000000000000..308173816f13 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 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.hdmi; + +/** + * Various utilities to handle HDMI CEC messages. + * + * TODO(b/110094868): unhide for Q + * @hide + */ +public class HdmiUtils { + + /** + * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)} + */ + static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1; + static final int TARGET_SAME_PHYSICAL_ADDRESS = 0; + + private HdmiUtils() { /* cannot be instantiated */ } + + /** + * Method to parse target physical address to the port number on the current device. + * + * <p>This check assumes target address is valid. + * + * @param targetPhysicalAddress is the physical address of the target device + * @param myPhysicalAddress is the physical address of the current device + * @return + * If the target device is under the current device, return the port number of current device + * that the target device is connected to. This also applies to the devices that are indirectly + * connected to the current device. + * + * <p>If the target device has the same physical address as the current device, return + * {@link #TARGET_SAME_PHYSICAL_ADDRESS}. + * + * <p>If the target device is not under the current device, return + * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}. + */ + public static int getLocalPortFromPhysicalAddress( + int targetPhysicalAddress, int myPhysicalAddress) { + if (myPhysicalAddress == targetPhysicalAddress) { + return TARGET_SAME_PHYSICAL_ADDRESS; + } + + int mask = 0xF000; + int finalMask = 0xF000; + int maskedAddress = myPhysicalAddress; + + while (maskedAddress != 0) { + maskedAddress = myPhysicalAddress & mask; + finalMask |= mask; + mask >>= 4; + } + + int portAddress = targetPhysicalAddress & finalMask; + if ((portAddress & (finalMask << 4)) != myPhysicalAddress) { + return TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + mask <<= 4; + int port = portAddress & mask; + while ((port >> 4) != 0) { + port >>= 4; + } + return port; + } +} diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 2b8d00b73b7e..1cd9920aa250 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -50,6 +50,7 @@ interface IHdmiControlService { List<HdmiPortInfo> getPortInfo(); boolean canChangeSystemAudioMode(); boolean getSystemAudioMode(); + int getPhysicalAddress(); void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback); void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener); void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener); @@ -60,6 +61,9 @@ interface IHdmiControlService { void setInputChangeListener(IHdmiInputChangeListener listener); List<HdmiDeviceInfo> getInputDevices(); List<HdmiDeviceInfo> getDeviceList(); + void powerOffRemoteDevice(int logicalAddress, int powerStatus); + void powerOnRemoteDevice(int logicalAddress, int powerStatus); + void askRemoteDeviceToBecomeActiveSource(int physicalAddress); void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, boolean hasVendorId); void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); @@ -73,4 +77,5 @@ interface IHdmiControlService { void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener); void setStandbyMode(boolean isStandbyModeOn); void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute); + void setSystemAudioModeOnForAudioOnlySource(); } diff --git a/core/java/android/net/IpPrefixParcelable.aidl b/core/java/android/net/IpPrefixParcelable.aidl new file mode 100644 index 000000000000..93a8d41936cc --- /dev/null +++ b/core/java/android/net/IpPrefixParcelable.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +parcelable IpPrefixParcelable { + String address; + int prefixLength; +}
\ No newline at end of file diff --git a/core/java/android/net/LinkAddressParcelable.aidl b/core/java/android/net/LinkAddressParcelable.aidl new file mode 100644 index 000000000000..af8e79b21f69 --- /dev/null +++ b/core/java/android/net/LinkAddressParcelable.aidl @@ -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. +*/ + +package android.net; + +parcelable LinkAddressParcelable { + String address; + int prefixLength; + int flags; + int scope; +}
\ No newline at end of file diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 617125b3f904..c2963fd605c0 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -191,6 +191,7 @@ public final class LinkProperties implements Parcelable { } setMtu(source.mMtu); mTcpBufferSizes = source.mTcpBufferSizes; + mNat64Prefix = source.mNat64Prefix; } } diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl new file mode 100644 index 000000000000..b153dc70e1b8 --- /dev/null +++ b/core/java/android/net/LinkPropertiesParcelable.aidl @@ -0,0 +1,39 @@ +/* + * 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.net.IpPrefixParcelable; +import android.net.LinkAddressParcelable; +import android.net.ProxyInfoParcelable; +import android.net.RouteInfoParcelable; + +parcelable LinkPropertiesParcelable { + String ifaceName; + LinkAddressParcelable[] linkAddresses; + String[] dnses; + String[] pcscfs; + String[] validatedPrivateDnses; + boolean usePrivateDns; + String privateDnsServerName; + String domains; + RouteInfoParcelable[] routes; + ProxyInfoParcelable httpProxy; + int mtu; + String tcpBufferSizes; + IpPrefixParcelable nat64Prefix; + LinkPropertiesParcelable[] stackedLinks; +}
\ No newline at end of file diff --git a/core/java/android/net/ProxyInfoParcelable.aidl b/core/java/android/net/ProxyInfoParcelable.aidl new file mode 100644 index 000000000000..59fd8467b820 --- /dev/null +++ b/core/java/android/net/ProxyInfoParcelable.aidl @@ -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. +*/ + +package android.net; + +parcelable ProxyInfoParcelable { + String host; + int port; + String[] exclusionList; + String pacFileUrl; +} diff --git a/core/java/android/net/RouteInfoParcelable.aidl b/core/java/android/net/RouteInfoParcelable.aidl new file mode 100644 index 000000000000..15bcdcfc2000 --- /dev/null +++ b/core/java/android/net/RouteInfoParcelable.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +import android.net.IpPrefixParcelable; + +parcelable RouteInfoParcelable { + IpPrefixParcelable destination; + String gatewayAddr; + String ifaceName; + int type; +} diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java index d7e5b2761e58..b932d2197f85 100644 --- a/core/java/android/net/ipmemorystore/NetworkAttributes.java +++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.StringJoiner; /** * A POD object to represent attributes of a single L2 network entry. @@ -207,4 +208,52 @@ public class NetworkAttributes { public int hashCode() { return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu); } + + /** Pretty print */ + @Override + public String toString() { + final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}"); + final ArrayList<String> nullFields = new ArrayList<>(); + + if (null != assignedV4Address) { + resultJoiner.add("assignedV4Addr :"); + resultJoiner.add(assignedV4Address.toString()); + } else { + nullFields.add("assignedV4Addr"); + } + + if (null != groupHint) { + resultJoiner.add("groupHint :"); + resultJoiner.add(groupHint); + } else { + nullFields.add("groupHint"); + } + + if (null != dnsAddresses) { + resultJoiner.add("dnsAddr : ["); + for (final InetAddress addr : dnsAddresses) { + resultJoiner.add(addr.getHostAddress()); + } + resultJoiner.add("]"); + } else { + nullFields.add("dnsAddr"); + } + + if (null != mtu) { + resultJoiner.add("mtu :"); + resultJoiner.add(mtu.toString()); + } else { + nullFields.add("mtu"); + } + + if (!nullFields.isEmpty()) { + resultJoiner.add("; Null fields : ["); + for (final String field : nullFields) { + resultJoiner.add(field); + } + resultJoiner.add("]"); + } + + return resultJoiner.toString(); + } } diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java index 0cb37e9f75b4..d040dcc3d608 100644 --- a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java +++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java @@ -128,4 +128,19 @@ public class SameL3NetworkResponse { public int hashCode() { return Objects.hash(l2Key1, l2Key2, confidence); } + + @Override + /** Pretty print */ + public String toString() { + switch (getNetworkSameness()) { + case NETWORK_SAME: + return "\"" + l2Key1 + "\" same L3 network as \"" + l2Key2 + "\""; + case NETWORK_DIFFERENT: + return "\"" + l2Key1 + "\" different L3 network from \"" + l2Key2 + "\""; + case NETWORK_NEVER_CONNECTED: + return "\"" + l2Key1 + "\" can't be tested against \"" + l2Key2 + "\""; + default: + return "Buggy sameness value ? \"" + l2Key1 + "\", \"" + l2Key2 + "\""; + } + } } diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java index 5b016ec55ae1..95e504224ac7 100644 --- a/core/java/android/net/ipmemorystore/Status.java +++ b/core/java/android/net/ipmemorystore/Status.java @@ -26,6 +26,8 @@ import android.annotation.NonNull; public class Status { public static final int SUCCESS = 0; + public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -1; + public final int resultCode; public Status(final int resultCode) { @@ -47,4 +49,14 @@ public class Status { public boolean isSuccess() { return SUCCESS == resultCode; } + + /** Pretty print */ + @Override + public String toString() { + switch (resultCode) { + case SUCCESS: return "SUCCESS"; + case ERROR_DATABASE_CANNOT_BE_OPENED: return "DATABASE CANNOT BE OPENED"; + default: return "Unknown value ?!"; + } + } } diff --git a/core/java/android/net/ipmemorystore/Utils.java b/core/java/android/net/ipmemorystore/Utils.java new file mode 100644 index 000000000000..73d8c83acdd9 --- /dev/null +++ b/core/java/android/net/ipmemorystore/Utils.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ipmemorystore; + +import android.annotation.NonNull; + +/** {@hide} */ +public class Utils { + /** Pretty print */ + public static String blobToString(final Blob blob) { + final StringBuilder sb = new StringBuilder("Blob : ["); + if (blob.data.length <= 24) { + appendByteArray(sb, blob.data, 0, blob.data.length); + } else { + appendByteArray(sb, blob.data, 0, 16); + sb.append("..."); + appendByteArray(sb, blob.data, blob.data.length - 8, blob.data.length); + } + sb.append("]"); + return sb.toString(); + } + + // Adds the hex representation of the array between the specified indices (inclusive, exclusive) + private static void appendByteArray(@NonNull final StringBuilder sb, @NonNull final byte[] ar, + final int from, final int to) { + for (int i = from; i < to; ++i) { + sb.append(String.format("%02X", ar[i])); + } + } +} diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index b15a4d3170b3..cbb3909a5536 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -114,7 +114,6 @@ public class BugreportManager { } } - // TODO(b/111441001) Connect up with BugreportListener methods. private final class DumpstateListener extends IDumpstateListener.Stub implements DeathRecipient { private final BugreportListener mListener; @@ -130,35 +129,35 @@ public class BugreportManager { @Override public void onProgress(int progress) throws RemoteException { - // TODO(b/111441001): implement + mListener.onProgress(progress); } @Override public void onError(int errorCode) throws RemoteException { - // TODO(b/111441001): implement + mListener.onError(errorCode); } @Override public void onFinished(long durationMs, String title, String description) throws RemoteException { - // TODO(b/111441001): implement + mListener.onFinished(durationMs, title, description); } // Old methods; should go away @Override public void onProgressUpdated(int progress) throws RemoteException { - // TODO(b/111441001): implement + // TODO(b/111441001): remove from interface } @Override public void onMaxProgressUpdated(int maxProgress) throws RemoteException { - // TODO(b/111441001): implement + // TODO(b/111441001): remove from interface } @Override public void onSectionComplete(String title, int status, int size, int durationMs) throws RemoteException { - // TODO(b/111441001): implement + // TODO(b/111441001): remove from interface } } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 9fea873d34a8..2d61a4ee95d9 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -289,6 +289,26 @@ public class Build { "ro.build.version.preview_sdk", 0); /** + * The SDK fingerprint for a given prerelease SDK. This value will always be + * {@code REL} on production platform builds/devices. + * + * <p>When this value is not {@code REL}, it contains a string fingerprint of the API + * surface exposed by the preview SDK. Preview platforms with different API surfaces + * will have different {@code PREVIEW_SDK_FINGERPRINT}. + * + * <p>This attribute is intended for use by installers for finer grained targeting of + * packages. Applications targeting preview APIs should not use this field and should + * instead use {@code PREVIEW_SDK_INT} or use reflection or other runtime checks to + * detect the presence of an API or guard themselves against unexpected runtime + * behavior. + * + * @hide + */ + @SystemApi + public static final String PREVIEW_SDK_FINGERPRINT = SystemProperties.get( + "ro.build.version.preview_sdk_fingerprint", "REL"); + + /** * The current development codename, or the string "REL" if this is * a release build. */ diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index b0b8f493e98a..f51ba9a41a2b 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -306,9 +306,18 @@ public class GraphicsEnvironment { String packageName, String paths, String devOptIn) { - // Check for temporary rules if debuggable or root - if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) { - Log.v(TAG, "Skipping loading temporary rules file"); + /** + * We only want to load a temp rules file for: + * - apps that are marked 'debuggable' in their manifest + * - devices that are running a userdebug build (ro.debuggable) or can inject libraries for + * debugging (PR_SET_DUMPABLE). + */ + boolean appIsDebuggable = isDebuggable(context); + boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1; + if (!(appIsDebuggable || deviceIsDebuggable)) { + Log.v(TAG, "Skipping loading temporary rules file: " + + "appIsDebuggable = " + appIsDebuggable + ", " + + "adbRootEnabled = " + deviceIsDebuggable); return false; } @@ -480,21 +489,36 @@ public class GraphicsEnvironment { return; } - if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS) - .contains(ai.packageName)) { + // GUP_DEV_ALL_APPS + // 0: Default (Invalid values fallback to default as well) + // 1: All apps use Game Update Package + // 2: All apps use system graphics driver + int gupDevAllApps = coreSettings.getInt(Settings.Global.GUP_DEV_ALL_APPS, 0); + if (gupDevAllApps == 2) { if (DEBUG) { - Log.w(TAG, ai.packageName + " opts out from GUP."); + Log.w(TAG, "GUP is turned off on this device"); } return; } - if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS) - .contains(ai.packageName) - && !onWhitelist(context, driverPackageName, ai.packageName)) { - if (DEBUG) { - Log.w(TAG, ai.packageName + " is not on the whitelist."); + if (gupDevAllApps != 1) { + // GUP_DEV_OPT_OUT_APPS has higher priority than GUP_DEV_OPT_IN_APPS + if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS) + .contains(ai.packageName)) { + if (DEBUG) { + Log.w(TAG, ai.packageName + " opts out from GUP."); + } + return; + } + + if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS) + .contains(ai.packageName) + && !onWhitelist(context, driverPackageName, ai.packageName)) { + if (DEBUG) { + Log.w(TAG, ai.packageName + " is not on the whitelist."); + } + return; } - return; } ApplicationInfo driverInfo; diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index fdd74882eb39..8ced7225b0d4 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -356,16 +356,6 @@ interface INetworkManagementService void removeVpnUidRanges(int netId, in UidRange[] ranges); /** - * Start the clatd (464xlat) service on the given interface. - */ - void startClatd(String interfaceName); - - /** - * Stop the clatd (464xlat) service on the given interface. - */ - void stopClatd(String interfaceName); - - /** * Start listening for mobile activity state changes. */ void registerNetworkActivityListener(INetworkActivityListener listener); diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index fb22194098b6..23c54f450a67 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -16,23 +16,27 @@ package android.os; -import java.util.Map; +import android.annotation.TestApi; +import java.util.Map; /** * Java API for libvintf. + * * @hide */ +@TestApi public class VintfObject { - /// ---------- OTA - /** * Slurps all device information (both manifests and both matrices) * and report them. * If any error in getting one of the manifests, it is not included in * the list. + * + * @hide */ + @TestApi public static native String[] report(); /** @@ -44,6 +48,8 @@ public class VintfObject { * @return = 0 if success (compatible) * > 0 if incompatible * < 0 if any error (mount partition fails, illformed XML, etc.) + * + * @hide */ public static native int verify(String[] packageInfo); @@ -55,22 +61,28 @@ public class VintfObject { * @return = 0 if success (compatible) * > 0 if incompatible * < 0 if any error (mount partition fails, illformed XML, etc.) + * + * @hide */ public static native int verifyWithoutAvb(); - /// ---------- CTS Device Info - /** * @return a list of HAL names and versions that is supported by this * device as stated in device and framework manifests. For example, * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0", * "android.hardware.camera.device@3.2"]. There are no duplicates. + * + * @hide */ + @TestApi public static native String[] getHalNamesAndVersions(); /** * @return the BOARD_SEPOLICY_VERS build flag available in device manifest. + * + * @hide */ + @TestApi public static native String getSepolicyVersion(); /** @@ -78,13 +90,22 @@ public class VintfObject { * specified in framework manifest. For example, * [("27", ["libjpeg.so", "libbase.so"]), * ("28", ["libjpeg.so", "libbase.so"])] + * + * @hide */ + @TestApi public static native Map<String, String[]> getVndkSnapshots(); /** - * @return target FCM version, a number specified in the device manifest - * indicating the FCM version that the device manifest implements. Null if - * device manifest doesn't specify this number (for legacy devices). + * @return Target Framework Compatibility Matrix (FCM) version, a number + * specified in the device manifest indicating the FCM version that the + * device manifest implements. Null if device manifest doesn't specify this + * number (for legacy devices). + * + * @hide */ + @TestApi public static native Long getTargetFrameworkCompatibilityMatrixVersion(); + + private VintfObject() {} } diff --git a/core/java/android/os/VintfRuntimeInfo.java b/core/java/android/os/VintfRuntimeInfo.java index 29698b9fa684..f17039ba9bf4 100644 --- a/core/java/android/os/VintfRuntimeInfo.java +++ b/core/java/android/os/VintfRuntimeInfo.java @@ -16,55 +16,84 @@ package android.os; +import android.annotation.TestApi; + /** * Java API for ::android::vintf::RuntimeInfo. Methods return null / 0 on any error. * * @hide */ +@TestApi public class VintfRuntimeInfo { private VintfRuntimeInfo() {} /** * @return /sys/fs/selinux/policyvers, via security_policyvers() native call + * + * @hide */ public static native long getKernelSepolicyVersion(); /** * @return content of /proc/cpuinfo + * + * @hide */ + @TestApi public static native String getCpuInfo(); /** * @return os name extracted from uname() native call + * + * @hide */ + @TestApi public static native String getOsName(); /** * @return node name extracted from uname() native call + * + * @hide */ + @TestApi public static native String getNodeName(); /** * @return os release extracted from uname() native call + * + * @hide */ + @TestApi public static native String getOsRelease(); /** * @return os version extracted from uname() native call + * + * @hide */ + @TestApi public static native String getOsVersion(); /** * @return hardware id extracted from uname() native call + * + * @hide */ + @TestApi public static native String getHardwareId(); /** * @return kernel version extracted from uname() native call. Format is * {@code x.y.z}. + * + * @hide */ + @TestApi public static native String getKernelVersion(); /** * @return libavb version in OS. Format is {@code x.y}. + * + * @hide */ public static native String getBootAvbVersion(); /** * @return libavb version in bootloader. Format is {@code x.y}. + * + * @hide */ public static native String getBootVbmetaAvbVersion(); - } diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 7a7bd83089f7..249b622dac4d 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -18,6 +18,8 @@ package android.permission; import android.os.RemoteCallback; import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; /** * Interface for system apps to communication with the permission controller. @@ -27,6 +29,7 @@ import android.os.Bundle; oneway interface IPermissionController { void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason, String callerPackageName, in RemoteCallback callback); + void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe); void getAppPermissions(String packageName, in RemoteCallback callback); void revokeRuntimePermission(String packageName, String permissionName); void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 0865b623028c..bfcca7c10151 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -36,10 +36,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; @@ -51,6 +53,11 @@ import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.util.Preconditions; +import libcore.io.IoUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -58,6 +65,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Interface for communicating with the permission controller. @@ -118,6 +126,20 @@ public final class PermissionControllerManager { } /** + * Callback for delivering the result of {@link #getRuntimePermissionBackup}. + * + * @hide + */ + public interface OnGetRuntimePermissionBackupCallback { + /** + * The result for {@link #getRuntimePermissionBackup}. + * + * @param backup The backup file + */ + void onGetRuntimePermissionsBackup(@NonNull byte[] backup); + } + + /** * Callback for delivering the result of {@link #getAppPermissions}. * * @hide @@ -219,6 +241,26 @@ public final class PermissionControllerManager { } /** + * Create a backup of the runtime permissions. + * + * @param user The user to be backed up + * @param executor Executor on which to invoke the callback + * @param callback Callback to receive the result + * + * @hide + */ + @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) + public void getRuntimePermissionBackup(@NonNull UserHandle user, + @NonNull @CallbackExecutor Executor executor, + @NonNull OnGetRuntimePermissionBackupCallback callback) { + checkNotNull(executor); + checkNotNull(callback); + + sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService, + user, executor, callback)); + } + + /** * Gets the runtime permissions for an app. * * @param packageName The package for which to query. @@ -355,6 +397,89 @@ public final class PermissionControllerManager { } /** + * Task to read a large amount of data from a remote service. + */ + private static class FileReaderTask<Callback extends Consumer<byte[]>> + extends AsyncTask<Void, Void, byte[]> { + private ParcelFileDescriptor mLocalPipe; + private ParcelFileDescriptor mRemotePipe; + + private final @NonNull Callback mCallback; + + FileReaderTask(@NonNull Callback callback) { + mCallback = callback; + } + + @Override + protected void onPreExecute() { + ParcelFileDescriptor[] pipe; + try { + pipe = ParcelFileDescriptor.createPipe(); + } catch (IOException e) { + Log.e(TAG, "Could not create pipe needed to get runtime permission backup", e); + return; + } + + mLocalPipe = pipe[0]; + mRemotePipe = pipe[1]; + } + + /** + * Get the file descriptor the remote service should write the data to. + * + * <p>Needs to be closed <u>locally</u> before the FileReader can finish. + * + * @return The file the data should be written to + */ + ParcelFileDescriptor getRemotePipe() { + return mRemotePipe; + } + + @Override + protected byte[] doInBackground(Void... ignored) { + ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream(); + + try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) { + byte[] buffer = new byte[16 * 1024]; + + while (!isCancelled()) { + int numRead = in.read(buffer); + if (numRead == -1) { + break; + } + + combinedBuffer.write(buffer, 0, numRead); + } + } catch (IOException | NullPointerException e) { + Log.e(TAG, "Error reading runtime permission backup", e); + combinedBuffer.reset(); + } + + return combinedBuffer.toByteArray(); + } + + /** + * Interrupt the reading of the data. + * + * <p>Needs to be called when canceling this task as it might be hung. + */ + void interruptRead() { + IoUtils.closeQuietly(mLocalPipe); + } + + @Override + protected void onCancelled() { + onPostExecute(new byte[]{}); + } + + @Override + protected void onPostExecute(byte[] backup) { + IoUtils.closeQuietly(mLocalPipe); + mCallback.accept(backup); + } + } + + /** * Request for {@link #revokeRuntimePermissions} */ private static final class PendingRevokeRuntimePermissionRequest extends @@ -441,6 +566,68 @@ public final class PermissionControllerManager { } /** + * Request for {@link #getRuntimePermissionBackup} + */ + private static final class PendingGetRuntimePermissionBackup extends + AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> + implements Consumer<byte[]> { + private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader; + private final @NonNull Executor mExecutor; + private final @NonNull OnGetRuntimePermissionBackupCallback mCallback; + private final @NonNull UserHandle mUser; + + private PendingGetRuntimePermissionBackup(@NonNull RemoteService service, + @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, + @NonNull OnGetRuntimePermissionBackupCallback callback) { + super(service); + + mUser = user; + mExecutor = executor; + mCallback = callback; + + mBackupReader = new FileReaderTask<>(this); + } + + @Override + protected void onTimeout(RemoteService remoteService) { + mBackupReader.cancel(true); + mBackupReader.interruptRead(); + } + + @Override + public void run() { + mBackupReader.execute(); + + ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe(); + try { + getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe); + } catch (RemoteException e) { + Log.e(TAG, "Error getting runtime permission backup", e); + } finally { + // Remote pipe end is duped by binder call. Local copy is not needed anymore + IoUtils.closeQuietly(remotePipe); + } + } + + /** + * Called when the {@link #mBackupReader} finished reading the file. + * + * @param backup The data read + */ + @Override + public void accept(byte[] backup) { + long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup)); + } finally { + Binder.restoreCallingIdentity(token); + } + + finish(); + } + } + + /** * Request for {@link #getAppPermissions} */ private static final class PendingGetAppPermissionRequest extends diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 75d61e6fe570..10e8c8d5b8dd 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -33,11 +33,16 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; +import android.os.UserHandle; import android.util.ArrayMap; +import android.util.Log; import com.android.internal.util.Preconditions; +import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -51,6 +56,7 @@ import java.util.Map; */ @SystemApi public abstract class PermissionControllerService extends Service { + private static final String LOG_TAG = PermissionControllerService.class.getSimpleName(); /** * The {@link Intent} action that must be declared as handled by a service @@ -83,6 +89,15 @@ public abstract class PermissionControllerService extends Service { @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName); /** + * Create a backup of the runtime permissions. + * + * @param user The user to back up + * @param out The stream to write the backup to + */ + public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user, + @NonNull OutputStream out); + + /** * Gets the runtime permissions for an app. * * @param packageName The package for which to query. @@ -163,6 +178,18 @@ public abstract class PermissionControllerService extends Service { } @Override + public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) { + checkNotNull(user); + checkNotNull(pipe); + + enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); + + mHandler.sendMessage(obtainMessage( + PermissionControllerService::getRuntimePermissionsBackup, + PermissionControllerService.this, user, pipe)); + } + + @Override public void getAppPermissions(String packageName, RemoteCallback callback) { checkNotNull(packageName, "packageName"); checkNotNull(callback, "callback"); @@ -237,6 +264,15 @@ public abstract class PermissionControllerService extends Service { callback.sendResult(result); } + private void getRuntimePermissionsBackup(@NonNull UserHandle user, + @NonNull ParcelFileDescriptor outFile) { + try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) { + onGetRuntimePermissionsBackup(user, out); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not open pipe to write backup tp", e); + } + } + private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); if (permissions != null && !permissions.isEmpty()) { diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index c79589577366..af8146e85ec0 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -16,9 +16,13 @@ package android.provider; +import static android.Manifest.permission.READ_DEVICE_CONFIG; +import static android.Manifest.permission.WRITE_DEVICE_CONFIG; + import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityThread; import android.content.ContentResolver; @@ -49,6 +53,13 @@ public final class DeviceConfig { public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config"); /** + * Namespace for all Game Driver features. + * @hide + */ + @SystemApi + public static final String NAMESPACE_GAME_DRIVER = "game_driver"; + + /** * Namespace for all input-related features that are used at the native level. * These features are applied at reboot. * @@ -78,6 +89,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(READ_DEVICE_CONFIG) public static String getProperty(String namespace, String name) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); String compositeName = createCompositeName(namespace, name); @@ -105,6 +117,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(WRITE_DEVICE_CONFIG) public static boolean setProperty( String namespace, String name, String value, boolean makeDefault) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); @@ -125,6 +138,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(WRITE_DEVICE_CONFIG) public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); Settings.Config.resetToDefaults(contentResolver, resetMode, namespace); @@ -146,10 +160,12 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener( @NonNull String namespace, @NonNull @CallbackExecutor Executor executor, @NonNull OnPropertyChangedListener onPropertyChangedListener) { + // TODO enforce READ_DEVICE_CONFIG permission synchronized (sLock) { Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener); if (oldNamespace == null) { diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java index 8e37559001db..e931826d2455 100644 --- a/core/java/android/provider/FontsContract.java +++ b/core/java/android/provider/FontsContract.java @@ -34,6 +34,7 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.net.Uri; +import android.os.Build.VERSION_CODES; import android.os.CancellationSignal; import android.os.Handler; import android.os.HandlerThread; @@ -651,7 +652,16 @@ public class FontsContract { if (familyBuilder == null) { familyBuilder = new FontFamily.Builder(font); } else { - familyBuilder.addFont(font); + try { + familyBuilder.addFont(font); + } catch (IllegalArgumentException e) { + if (context.getApplicationInfo().targetSdkVersion <= VERSION_CODES.P) { + // Surpress the IllegalArgumentException for keeping the backward + // compatibility. + continue; + } + throw e; + } } } catch (IOException e) { continue; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 64aa062a3c49..b39d871c8637 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7391,6 +7391,14 @@ public final class Settings { private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** + * Whether the device should respond to the SLPI tap gesture. + * @hide + */ + public static final String DOZE_TAP_SCREEN_GESTURE = "doze_tap_gesture"; + + private static final Validator DOZE_TAP_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * Gesture that wakes up the lock screen. * @hide */ @@ -7407,6 +7415,22 @@ public final class Settings { private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** + * Gesture that skips media. + * @hide + */ + public static final String SKIP_GESTURE = "skip_gesture"; + + private static final Validator SKIP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** + * Gesture that silences sound (alarms, notification, calls). + * @hide + */ + public static final String SILENCE_GESTURE = "silence_gesture"; + + private static final Validator SILENCE_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -7708,6 +7732,23 @@ public final class Settings { public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels"; /** + * Whether TV app uses non-system inputs. + * + * <p> + * The value is boolean (1 or 0), where 1 means non-system TV inputs are allowed, + * and 0 means non-system TV inputs are not allowed. + * + * <p> + * Devices such as sound bars may have changed the system property allow_third_party_inputs + * to false so the TV Application only uses HDMI and other built in inputs. This setting + * allows user to override the default and have the TV Application use third party TV inputs + * available on play store. + * + * @hide + */ + public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs"; + + /** * Whether automatic routing of system audio to USB audio peripheral is disabled. * The value is boolean (1 or 0), where 1 means automatic routing is disabled, * and 0 means automatic routing is enabled. @@ -8445,6 +8486,7 @@ public final class Settings { DOZE_ALWAYS_ON, DOZE_PICK_UP_GESTURE, DOZE_DOUBLE_TAP_GESTURE, + DOZE_TAP_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE, NFC_PAYMENT_DEFAULT_COMPONENT, @@ -8483,6 +8525,8 @@ public final class Settings { NOTIFICATION_NEW_INTERRUPTION_MODEL, TRUST_AGENTS_EXTEND_UNLOCK, LOCK_SCREEN_WHEN_TRUST_LOST, + SKIP_GESTURE, + SILENCE_GESTURE, }; /** @@ -8599,6 +8643,7 @@ public final class Settings { VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR); VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR); + VALIDATORS.put(DOZE_TAP_SCREEN_GESTURE, DOZE_TAP_SCREEN_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR); VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR); @@ -8650,6 +8695,8 @@ public final class Settings { VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR); VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR); VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR); + VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR); + VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR); } /** @@ -9361,6 +9408,15 @@ public final class Settings { "hdmi_system_audio_control_enabled"; /** + * Whether HDMI Routing Control feature is enabled. If enabled, the switch device will + * route to the correct input source on receiving Routing Control related messages. If + * disabled, you can only switch the input via controls on this device. + * @hide + */ + public static final String HDMI_CEC_SWITCH_ENABLED = + "hdmi_cec_switch_enabled"; + + /** * Whether TV will automatically turn on upon reception of the CEC command * <Text View On> or <Image View On>. (0 = false, 1 = true) * @@ -11062,6 +11118,31 @@ public final class Settings { /** {@hide} */ public static final String BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_"; + /** + * Enable/disable radio bug detection + * + * {@hide} + */ + public static final String + ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection"; + + /** + * Count threshold of RIL wakelock timeout for radio bug detection + * + * {@hide} + */ + public static final String + RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD = + "radio_bug_wakelock_timeout_count_threshold"; + + /** + * Count threshold of RIL system error for radio bug detection + * + * {@hide} + */ + public static final String + RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD = + "radio_bug_system_error_count_threshold"; /** * Activity manager specific settings. @@ -11552,6 +11633,7 @@ public final class Settings { * battery_level_collection_delay_ms (long) * max_history_files (int) * max_history_buffer_kb (int) + * battery_charged_delay_ms (int) * </pre> * * <p> @@ -11937,22 +12019,33 @@ public final class Settings { "angle_gl_driver_selection_values"; /** - * List of Apps selected to use Game Update Packages. + * Game Update Package global preference for all Apps. + * 0 = Default + * 1 = All Apps use Game Update Package + * 2 = All Apps use system graphics driver + * @hide + */ + public static final String GUP_DEV_ALL_APPS = "gup_dev_all_apps"; + + /** + * List of Apps selected to use Game Update Package. + * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps"; /** - * List of Apps selected not to use Game Update Packages. + * List of Apps selected not to use Game Update Package. + * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps"; /** - * Apps on the black list that are forbidden to useGame Update Package. + * Apps on the blacklist that are forbidden to use Game Update Package. * @hide */ - public static final String GUP_BLACK_LIST = "gup_black_list"; + public static final String GUP_BLACKLIST = "gup_blacklist"; /** * Ordered GPU debug layer list for Vulkan @@ -14033,7 +14126,7 @@ public final class Settings { * * @hide */ - // TODO(b/117663715): require a new read permission + @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) static String getString(ContentResolver resolver, String name) { return sNameValueCache.getStringForUser(resolver, name, resolver.getUserId()); } @@ -14056,8 +14149,7 @@ public final class Settings { * * @hide */ - // TODO(b/117663715): require a new write permission restricted to a single source - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) static boolean putString(@NonNull ContentResolver resolver, @NonNull String name, @Nullable String value, boolean makeDefault) { return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault, @@ -14079,7 +14171,7 @@ public final class Settings { * @hide */ // TODO(b/117663715): require a new write permission restricted to a single source - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode, @Nullable String prefix) { try { diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 01169612a201..aaba85bd36a7 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -42,6 +42,7 @@ import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAugmentedAutofillManagerClient; +import android.view.autofill.IAutofillWindowPresenter; import com.android.internal.annotations.GuardedBy; @@ -95,10 +96,10 @@ public abstract class AugmentedAutofillService extends Service { } @Override - public void onDestroyFillWindowRequest(int sessionId) { + public void onDestroyAllFillWindowsRequest() { mHandler.sendMessage( - obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest, - AugmentedAutofillService.this, sessionId)); + obtainMessage(AugmentedAutofillService::handleOnDestroyAllFillWindowsRequest, + AugmentedAutofillService.this)); } }; @@ -185,18 +186,21 @@ public abstract class AugmentedAutofillService extends Service { new FillCallback(proxy)); } - private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) { - AutofillProxy proxy = null; + private void handleOnDestroyAllFillWindowsRequest() { if (mAutofillProxies != null) { - proxy = mAutofillProxies.get(sessionId); - } - if (proxy == null) { - // TODO(b/111330312): this might be fine, in which case we should logv it - Log.w(TAG, "No proxy for session " + sessionId); - return; + final int size = mAutofillProxies.size(); + for (int i = 0; i < size; i++) { + final int sessionId = mAutofillProxies.keyAt(i); + final AutofillProxy proxy = mAutofillProxies.valueAt(i); + if (proxy == null) { + // TODO(b/111330312): this might be fine, in which case we should logv it + Log.w(TAG, "No proxy for session " + sessionId); + return; + } + proxy.destroy(); + } + mAutofillProxies.clear(); } - proxy.destroy(); - mAutofillProxies.remove(sessionId); } private void handleOnUnbind() { @@ -350,6 +354,16 @@ public abstract class AugmentedAutofillService extends Service { } } + public void requestShowFillUi(int width, int height, Rect anchorBounds, + IAutofillWindowPresenter presenter) throws RemoteException { + mClient.requestShowFillUi(mSessionId, mFocusedId, width, height, anchorBounds, + presenter); + } + + public void requestHideFillUi() throws RemoteException { + mClient.requestHideFillUi(mSessionId, mFocusedId); + } + private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) { synchronized (mLock) { // TODO(b/111330312): should we close the popupwindow if the focused id changed? diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 33b88e42edb7..51b0f01af6ae 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -16,22 +16,25 @@ package android.service.autofill.augmented; import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG; +import static android.service.autofill.augmented.AugmentedAutofillService.VERBOSE; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.app.Dialog; import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy; import android.service.autofill.augmented.PresentationParams.Area; import android.util.Log; -import android.view.Gravity; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; -import android.view.Window; import android.view.WindowManager; +import android.view.autofill.IAutofillWindowPresenter; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; @@ -71,7 +74,7 @@ public final class FillWindow implements AutoCloseable { /** Indicates the data being shown is a physical address */ public static final long FLAG_METADATA_ADDRESS = 0x1; - // TODO(b/111330312): add moar flags + // TODO(b/111330312): add more flags /** @hide */ @LongDef(prefix = { "FLAG" }, value = { @@ -83,8 +86,17 @@ public final class FillWindow implements AutoCloseable { private final Object mLock = new Object(); private final CloseGuard mCloseGuard = CloseGuard.get(); + private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper()); + private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter(); + + @GuardedBy("mLock") + private WindowManager mWm; @GuardedBy("mLock") - private Dialog mDialog; + private View mFillView; + @GuardedBy("mLock") + private boolean mShowing; + @GuardedBy("mLock") + private Rect mBounds; @GuardedBy("mLock") private boolean mDestroyed; @@ -140,51 +152,28 @@ public final class FillWindow implements AutoCloseable { // window instead of destroying. In fact, it might be better to allocate a full window // initially, which is transparent (and let touches get through) everywhere but in the // rect boundaries. - destroy(); // TODO(b/111330312): make sure all touch events are handled, window is always closed, // etc. - mDialog = new Dialog(rootView.getContext()) { - @Override - public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { - FillWindow.this.destroy(); + mWm = rootView.getContext().getSystemService(WindowManager.class); + mFillView = rootView; + // Listen to the touch outside to destroy the window when typing is detected. + mFillView.setOnTouchListener( + (view, motionEvent) -> { + if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) { + if (VERBOSE) Log.v(TAG, "Outside touch detected, hiding the window"); + hide(); + } + return false; } - return false; - } - }; - mCloseGuard.open("destroy"); - final Window window = mDialog.getWindow(); - window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); - // Makes sure touch outside the dialog is received by the window behind the dialog. - window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); - // Makes sure the touch outside the dialog is received by the dialog to dismiss it. - window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); - // Makes sure keyboard shows up. - window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); - - final int height = rect.bottom - rect.top; - final int width = rect.right - rect.left; - final WindowManager.LayoutParams windowParams = window.getAttributes(); - windowParams.gravity = Gravity.TOP | Gravity.LEFT; - windowParams.y = rect.top + height; - windowParams.height = height; - windowParams.x = rect.left; - windowParams.width = width; - - window.setAttributes(windowParams); - window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); - window.setBackgroundDrawableResource(android.R.color.transparent); - - mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height); - mDialog.setContentView(rootView, diagParams); - + ); + mShowing = false; + mBounds = new Rect(area.getBounds()); if (DEBUG) { Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView); } - + mDestroyed = false; mProxy.setFillWindow(this); return true; } @@ -194,36 +183,87 @@ public final class FillWindow implements AutoCloseable { void show() { // TODO(b/111330312): check if updated first / throw exception if (DEBUG) Log.d(TAG, "show()"); - synchronized (mLock) { checkNotDestroyedLocked(); - if (mDialog == null) { + if (mWm == null || mFillView == null) { throw new IllegalStateException("update() not called yet, or already destroyed()"); } - - mDialog.show(); if (mProxy != null) { + try { + mProxy.requestShowFillUi(mBounds.right - mBounds.left, + mBounds.bottom - mBounds.top, + /*anchorBounds=*/ null, mFillWindowPresenter); + } catch (RemoteException e) { + Log.w(TAG, "Error requesting to show fill window", e); + } mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN); } } } /** - * Destroys the window. + * Hides the window. * - * <p>Once destroyed, this window cannot be used anymore + * <p>The window is not destroyed and can be shown again */ - public void destroy() { - if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog); + private void hide() { + if (DEBUG) Log.d(TAG, "hide()"); + synchronized (mLock) { + checkNotDestroyedLocked(); + if (mWm == null || mFillView == null) { + throw new IllegalStateException("update() not called yet, or already destroyed()"); + } + if (mProxy != null && mShowing) { + try { + mProxy.requestHideFillUi(); + } catch (RemoteException e) { + Log.w(TAG, "Error requesting to hide fill window", e); + } + } + } + } - synchronized (this) { - if (mDestroyed || mDialog == null) return; + private void handleShow(WindowManager.LayoutParams p) { + if (DEBUG) Log.d(TAG, "handleShow()"); + synchronized (mLock) { + if (mWm != null && mFillView != null) { + p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; + if (!mShowing) { + mWm.addView(mFillView, p); + mShowing = true; + } else { + mWm.updateViewLayout(mFillView, p); + } + } + } + } - mDialog.dismiss(); - mDialog = null; - if (mProxy != null) { - mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); + private void handleHide() { + if (DEBUG) Log.d(TAG, "handleHide()"); + synchronized (mLock) { + if (mWm != null && mFillView != null && mShowing) { + mWm.removeView(mFillView); + mShowing = false; } + } + } + + /** + * Destroys the window. + * + * <p>Once destroyed, this window cannot be used anymore + */ + public void destroy() { + if (DEBUG) { + Log.d(TAG, + "destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView=" + + mFillView); + } + synchronized (mLock) { + if (mDestroyed) return; + hide(); + mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); + mDestroyed = true; mCloseGuard.close(); } } @@ -250,11 +290,15 @@ public final class FillWindow implements AutoCloseable { public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { synchronized (this) { pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); - if (mDialog != null) { - pw.print(prefix); pw.print("dialog: "); - pw.println(mDialog.isShowing() ? "shown" : "hidden"); - pw.print(prefix); pw.print("window: "); - pw.println(mDialog.getWindow().getAttributes()); + if (mFillView != null) { + pw.print(prefix); pw.print("fill window: "); + pw.println(mShowing ? "shown" : "hidden"); + pw.print(prefix); pw.print("fill view: "); + pw.println(mFillView); + pw.print(prefix); pw.print("mBounds: "); + pw.println(mBounds); + pw.print(prefix); pw.print("mWm: "); + pw.println(mWm); } } } @@ -264,4 +308,19 @@ public final class FillWindow implements AutoCloseable { public void close() throws Exception { destroy(); } + + private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub { + @Override + public void show(WindowManager.LayoutParams p, Rect transitionEpicenter, + boolean fitsSystemWindows, int layoutDirection) { + if (DEBUG) Log.d(TAG, "FillWindowPresenter.show()"); + mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p)); + } + + @Override + public void hide(Rect transitionEpicenter) { + if (DEBUG) Log.d(TAG, "FillWindowPresenter.hide()"); + mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this)); + } + } } diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl index b3ac2da1c17e..fb6912ac6752 100644 --- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl +++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl @@ -36,5 +36,5 @@ oneway interface IAugmentedAutofillService { in ComponentName activityComponent, in AutofillId focusedId, in AutofillValue focusedValue, long requestTime, in IFillCallback callback); - void onDestroyFillWindowRequest(int sessionId); + void onDestroyAllFillWindowsRequest(); } diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index e5e028d22d97..302e1a656833 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -48,6 +48,7 @@ import com.android.internal.os.IResultReceiver; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -77,6 +78,7 @@ public abstract class ContentCaptureService extends Service { "android.service.contentcapture.ContentCaptureService"; private Handler mHandler; + private IContentCaptureServiceCallback mCallback; /** * Binder that receives calls from the system server. @@ -84,9 +86,15 @@ public abstract class ContentCaptureService extends Service { private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() { @Override - public void onConnectedStateChanged(boolean state) { - mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnectedStateChanged, - ContentCaptureService.this, state)); + public void onConnected(IBinder callback) { + mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnected, + ContentCaptureService.this, callback)); + } + + @Override + public void onDisconnected() { + mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDisconnected, + ContentCaptureService.this)); } @Override @@ -166,7 +174,16 @@ public abstract class ContentCaptureService extends Service { */ public final void setContentCaptureWhitelist(@Nullable List<String> packages, @Nullable List<ComponentName> activities) { - //TODO(b/122595322): implement + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "setContentCaptureWhitelist(): no server callback"); + return; + } + try { + callback.setContentCaptureWhitelist(packages, activities); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -177,7 +194,16 @@ public abstract class ContentCaptureService extends Service { */ public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity, boolean enabled) { - //TODO(b/122595322): implement + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "setActivityContentCaptureEnabled(): no server callback"); + return; + } + try { + callback.setActivityContentCaptureEnabled(activity, enabled); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -188,7 +214,16 @@ public abstract class ContentCaptureService extends Service { */ public final void setPackageContentCaptureEnabled(@NonNull String packageName, boolean enabled) { - //TODO(b/122595322): implement + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "setPackageContentCaptureEnabled(): no server callback"); + return; + } + try { + callback.setPackageContentCaptureEnabled(packageName, enabled); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -197,7 +232,12 @@ public abstract class ContentCaptureService extends Service { */ @NonNull public final Set<ComponentName> getContentCaptureDisabledActivities() { - //TODO(b/122595322): implement + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "getContentCaptureDisabledActivities(): no server callback"); + return Collections.emptySet(); + } + //TODO(b/122595322): implement (using SyncResultReceiver) return null; } @@ -207,7 +247,12 @@ public abstract class ContentCaptureService extends Service { */ @NonNull public final Set<String> getContentCaptureDisabledPackages() { - //TODO(b/122595322): implement + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "getContentCaptureDisabledPackages(): no server callback"); + return Collections.emptySet(); + } + //TODO(b/122595322): implement (using SyncResultReceiver) return null; } @@ -307,12 +352,14 @@ public abstract class ContentCaptureService extends Service { } } - private void handleOnConnectedStateChanged(boolean state) { - if (state) { - onConnected(); - } else { - onDisconnected(); - } + private void handleOnConnected(@NonNull IBinder callback) { + mCallback = IContentCaptureServiceCallback.Stub.asInterface(callback); + onConnected(); + } + + private void handleOnDisconnected() { + onDisconnected(); + mCallback = null; } //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session, @@ -323,15 +370,21 @@ public abstract class ContentCaptureService extends Service { mSessionUids.put(sessionId, uid); onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId)); - final int flags = context.getFlags(); - if ((flags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) { - setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_BY_FLAG_SECURE, - mClientInterface.asBinder()); - return; + final int clientFlags = context.getFlags(); + int stateFlags = 0; + if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) { + stateFlags |= ContentCaptureSession.STATE_FLAG_SECURE; } + if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0) { + stateFlags |= ContentCaptureSession.STATE_BY_APP; + } + if (stateFlags == 0) { + stateFlags = ContentCaptureSession.STATE_ACTIVE; + } else { + stateFlags |= ContentCaptureSession.STATE_DISABLED; - setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE, - mClientInterface.asBinder()); + } + setClientState(clientReceiver, stateFlags, mClientInterface.asBinder()); } private void handleSendEvents(int uid, diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl index 1b4cccf630a0..a8dd21392337 100644 --- a/core/java/android/service/contentcapture/IContentCaptureService.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl @@ -16,6 +16,7 @@ package android.service.contentcapture; +import android.os.IBinder; import android.service.contentcapture.SnapshotData; import android.view.contentcapture.ContentCaptureContext; @@ -29,7 +30,8 @@ import java.util.List; * @hide */ oneway interface IContentCaptureService { - void onConnectedStateChanged(boolean state); + void onConnected(IBinder callback); + void onDisconnected(); void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid, in IResultReceiver clientReceiver); void onSessionFinished(String sessionId); diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl new file mode 100644 index 000000000000..e84bd6fdf2b6 --- /dev/null +++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl @@ -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.service.contentcapture; + +import android.content.ComponentName; +import com.android.internal.os.IResultReceiver; + +import java.util.List; + +/** + * Interface from the Content Capture service to the system. + * + * @hide + */ +oneway interface IContentCaptureServiceCallback { + void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities); + void setActivityContentCaptureEnabled(in ComponentName activity, boolean enabled); + void setPackageContentCaptureEnabled(in String packageName, boolean enabled); + void getContentCaptureDisabledActivities(in IResultReceiver receiver); + void getContentCaptureDisabledPackages(in IResultReceiver receiver); +} diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 163e3d5fab56..6f274477431f 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -121,6 +121,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall */ public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6; + /** + * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked + * from an Android automotive system Ui. + */ + public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7; + final Context mContext; final HandlerCaller mHandlerCaller; diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 85b6b889be33..44353b1609b7 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -33,8 +33,7 @@ import android.icu.text.Edits; import android.icu.util.ULocale; import android.os.Parcel; import android.os.Parcelable; -import android.os.SystemProperties; -import android.provider.Settings; +import android.sysprop.DisplayProperties; import android.text.style.AbsoluteSizeSpan; import android.text.style.AccessibilityClickableSpan; import android.text.style.AccessibilityURLSpan; @@ -2059,7 +2058,7 @@ public class TextUtils { return ((locale != null && !locale.equals(Locale.ROOT) && ULocale.forLocale(locale).isRightToLeft()) // If forcing into RTL layout mode, return RTL as default - || SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false)) + || DisplayProperties.debug_force_rtl().orElse(false)) ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR; } diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index ea4464daf1e2..99106bedb314 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -216,6 +216,12 @@ public class TypedValue { * */ public int density; + /** + * If the Value came from a style resource, this holds the corresponding style resource id + * against which the attribute was resolved. + */ + public int sourceStyleResourceId; + /* ------------------------------------------------------------ */ /** Return the data for this value as a float. Only use for values diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index ffd4156b5bfd..dc7c343c2c3e 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -29,6 +30,7 @@ import android.graphics.Canvas; import android.os.Build; import android.os.Handler; import android.os.Message; +import android.os.SystemProperties; import android.os.Trace; import android.util.AttributeSet; import android.util.Log; @@ -38,6 +40,9 @@ import android.widget.FrameLayout; import com.android.internal.R; +import dalvik.system.PathClassLoader; +import java.io.File; +import java.lang.reflect.Method; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -72,6 +77,10 @@ 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. */ private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; @@ -93,6 +102,13 @@ public abstract class LayoutInflater { private Factory2 mPrivateFactory; private Filter mFilter; + // Indicates whether we should try to inflate layouts using a precompiled layout instead of + // inflating from the XML resource. + private boolean mUseCompiledView; + // This variable holds the classloader that will be used to look for precompiled layouts. The + // The classloader includes the generated compiled_view.dex file. + private ClassLoader mPrecompiledClassLoader; + @UnsupportedAppUsage final Object[] mConstructorArgs = new Object[2]; @@ -223,6 +239,7 @@ public abstract class LayoutInflater { */ protected LayoutInflater(Context context) { mContext = context; + initPrecompiledViews(); } /** @@ -239,6 +256,7 @@ public abstract class LayoutInflater { mFactory2 = original.mFactory2; mPrivateFactory = original.mPrivateFactory; setFilter(original.mFilter); + initPrecompiledViews(); } /** @@ -380,6 +398,41 @@ public abstract class LayoutInflater { } } + private void initPrecompiledViews() { + // Check if precompiled layouts are enabled by a system property. + mUseCompiledView = + SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false); + if (!mUseCompiledView) { + return; + } + + // Make sure the application allows code generation + ApplicationInfo appInfo = mContext.getApplicationInfo(); + if ((appInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0 + || appInfo.isPrivilegedApp()) { + mUseCompiledView = false; + return; + } + + // Try to load the precompiled layout file. + try { + mPrecompiledClassLoader = mContext.getClassLoader(); + String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME; + if (new File(dexFile).exists()) { + mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader); + } else { + // If the precompiled layout file doesn't exist, then disable precompiled + // layouts. + mUseCompiledView = false; + } + } catch (Throwable e) { + if (DEBUG) { + Log.e(TAG, "Failed to initialized precompiled views layouts", e); + } + mUseCompiledView = false; + } + } + /** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. @@ -436,10 +489,14 @@ public abstract class LayoutInflater { final Resources res = getContext().getResources(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" - + Integer.toHexString(resource) + ")"); + + Integer.toHexString(resource) + ")"); } - final XmlResourceParser parser = res.getLayout(resource); + View view = tryInflatePrecompiled(resource, res, root, attachToRoot); + if (view != null) { + return view; + } + XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { @@ -447,6 +504,73 @@ public abstract class LayoutInflater { } } + private @Nullable + View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root, + boolean attachToRoot) { + if (!mUseCompiledView) { + return null; + } + + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)"); + + // Try to inflate using a precompiled layout. + String pkg = res.getResourcePackageName(resource); + String layout = res.getResourceEntryName(resource); + + try { + Class clazz = mPrecompiledClassLoader.loadClass("" + pkg + ".CompiledView"); + Method inflater = clazz.getMethod(layout, Context.class, int.class); + View view = (View) inflater.invoke(null, mContext, resource); + + if (view != null && root != null) { + // We were able to use the precompiled inflater, but now we need to do some work to + // attach the view to the root correctly. + XmlResourceParser parser = res.getLayout(resource); + try { + AttributeSet attrs = Xml.asAttributeSet(parser); + advanceToRootNode(parser); + ViewGroup.LayoutParams params = root.generateLayoutParams(attrs); + + if (attachToRoot) { + root.addView(view, params); + } else { + view.setLayoutParams(params); + } + } finally { + parser.close(); + } + } + + return view; + } catch (Throwable e) { + if (DEBUG) { + Log.e(TAG, "Failed to use precompiled view", e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + return null; + } + + /** + * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is + * found. + */ + private void advanceToRootNode(XmlPullParser parser) + throws InflateException, IOException, XmlPullParserException { + // Look for the root node. + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty + } + + if (type != XmlPullParser.START_TAG) { + throw new InflateException(parser.getPositionDescription() + + ": No start tag found!"); + } + } + /** * Inflate a new view hierarchy from the specified XML node. Throws * {@link InflateException} if there is an error. @@ -480,18 +604,7 @@ public abstract class LayoutInflater { View result = root; try { - // Look for the root node. - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG && - type != XmlPullParser.END_DOCUMENT) { - // Empty - } - - if (type != XmlPullParser.START_TAG) { - throw new InflateException(parser.getPositionDescription() - + ": No start tag found!"); - } - + advanceToRootNode(parser); final String name = parser.getName(); if (DEBUG) { @@ -994,82 +1107,85 @@ public abstract class LayoutInflater { + "reference. The layout ID " + value + " is not valid."); } - final XmlResourceParser childParser = context.getResources().getLayout(layout); + final View precompiled = tryInflatePrecompiled(layout, context.getResources(), + (ViewGroup) parent, /*attachToRoot=*/true); + if (precompiled == null) { + final XmlResourceParser childParser = context.getResources().getLayout(layout); - try { - final AttributeSet childAttrs = Xml.asAttributeSet(childParser); + try { + final AttributeSet childAttrs = Xml.asAttributeSet(childParser); - while ((type = childParser.next()) != XmlPullParser.START_TAG && - type != XmlPullParser.END_DOCUMENT) { - // Empty. - } + while ((type = childParser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty. + } - if (type != XmlPullParser.START_TAG) { - throw new InflateException(childParser.getPositionDescription() + - ": No start tag found!"); - } + if (type != XmlPullParser.START_TAG) { + throw new InflateException(childParser.getPositionDescription() + + ": No start tag found!"); + } - final String childName = childParser.getName(); + final String childName = childParser.getName(); - if (TAG_MERGE.equals(childName)) { - // The <merge> tag doesn't support android:theme, so - // nothing special to do here. - rInflate(childParser, parent, context, childAttrs, false); - } else { - final View view = createViewFromTag(parent, childName, - context, childAttrs, hasThemeOverride); - final ViewGroup group = (ViewGroup) parent; - - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.Include); - final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID); - final int visibility = a.getInt(R.styleable.Include_visibility, -1); - a.recycle(); - - // We try to load the layout params set in the <include /> tag. - // If the parent can't generate layout params (ex. missing width - // or height for the framework ViewGroups, though this is not - // necessarily true of all ViewGroups) then we expect it to throw - // a runtime exception. - // We catch this exception and set localParams accordingly: true - // means we successfully loaded layout params from the <include> - // tag, false means we need to rely on the included layout params. - ViewGroup.LayoutParams params = null; - try { - params = group.generateLayoutParams(attrs); - } catch (RuntimeException e) { - // Ignore, just fail over to child attrs. - } - if (params == null) { - params = group.generateLayoutParams(childAttrs); - } - view.setLayoutParams(params); + if (TAG_MERGE.equals(childName)) { + // The <merge> tag doesn't support android:theme, so + // nothing special to do here. + rInflate(childParser, parent, context, childAttrs, false); + } else { + final View view = createViewFromTag(parent, childName, + context, childAttrs, hasThemeOverride); + final ViewGroup group = (ViewGroup) parent; + + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.Include); + final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID); + final int visibility = a.getInt(R.styleable.Include_visibility, -1); + a.recycle(); + + // We try to load the layout params set in the <include /> tag. + // If the parent can't generate layout params (ex. missing width + // or height for the framework ViewGroups, though this is not + // necessarily true of all ViewGroups) then we expect it to throw + // a runtime exception. + // We catch this exception and set localParams accordingly: true + // means we successfully loaded layout params from the <include> + // tag, false means we need to rely on the included layout params. + ViewGroup.LayoutParams params = null; + try { + params = group.generateLayoutParams(attrs); + } catch (RuntimeException e) { + // Ignore, just fail over to child attrs. + } + if (params == null) { + params = group.generateLayoutParams(childAttrs); + } + view.setLayoutParams(params); - // Inflate all children. - rInflateChildren(childParser, view, childAttrs, true); + // Inflate all children. + rInflateChildren(childParser, view, childAttrs, true); - if (id != View.NO_ID) { - view.setId(id); - } + if (id != View.NO_ID) { + view.setId(id); + } - switch (visibility) { - case 0: - view.setVisibility(View.VISIBLE); - break; - case 1: - view.setVisibility(View.INVISIBLE); - break; - case 2: - view.setVisibility(View.GONE); - break; - } + switch (visibility) { + case 0: + view.setVisibility(View.VISIBLE); + break; + case 1: + view.setVisibility(View.INVISIBLE); + break; + case 2: + view.setVisibility(View.GONE); + break; + } - group.addView(view); + group.addView(view); + } + } finally { + childParser.close(); } - } finally { - childParser.close(); } - LayoutInflater.consumeChildElements(parser); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index ff5120d09e25..5e98236f7535 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -30,6 +30,7 @@ import static android.view.SurfaceControlProto.NAME; import android.annotation.Size; import android.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.PixelFormat; @@ -141,6 +142,7 @@ public class SurfaceControl implements Parcelable { private static native int nativeGetActiveConfig(IBinder displayToken); private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); private static native int[] nativeGetDisplayColorModes(IBinder displayToken); + private static native int[] nativeGetCompositionDataspaces(); private static native int nativeGetActiveColorMode(IBinder displayToken); private static native boolean nativeSetActiveColorMode(IBinder displayToken, int colorMode); @@ -167,6 +169,7 @@ public class SurfaceControl implements Parcelable { InputWindowHandle handle); private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken, IBinder toToken); + private static native boolean nativeGetProtectedContentSupport(); private final CloseGuard mCloseGuard = CloseGuard.get(); private String mName; @@ -374,6 +377,13 @@ public class SurfaceControl implements Parcelable { */ public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731; + /** + * internal representation of how to interpret pixel value, used only to convert to ColorSpace. + */ + private static final int INTERNAL_DATASPACE_SRGB = 142671872; + private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696; + private static final int INTERNAL_DATASPACE_SCRGB = 411107328; + private void assignNativeObject(long nativeObject) { if (mNativeObject != 0) { release(); @@ -1516,6 +1526,35 @@ public class SurfaceControl implements Parcelable { } /** + * Returns an array of color spaces with 2 elements. The first color space is the + * default color space and second one is wide color gamut color space. + * @hide + */ + public static ColorSpace[] getCompositionColorSpaces() { + int[] dataspaces = nativeGetCompositionDataspaces(); + ColorSpace srgb = ColorSpace.get(ColorSpace.Named.SRGB); + ColorSpace[] colorSpaces = { srgb, srgb }; + if (dataspaces.length == 2) { + for (int i = 0; i < 2; ++i) { + switch(dataspaces[i]) { + case INTERNAL_DATASPACE_DISPLAY_P3: + colorSpaces[i] = ColorSpace.get(ColorSpace.Named.DISPLAY_P3); + break; + case INTERNAL_DATASPACE_SCRGB: + colorSpaces[i] = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB); + break; + case INTERNAL_DATASPACE_SRGB: + // Other dataspace is not recognized, use SRGB color space instead, + // the default value of the array is already SRGB, thus do nothing. + default: + break; + } + } + } + return colorSpaces; + } + + /** * @hide */ @UnsupportedAppUsage @@ -1730,6 +1769,14 @@ public class SurfaceControl implements Parcelable { } /** + * Returns whether protected content is supported in GPU composition. + * @hide + */ + public static boolean getProtectedContentSupport() { + return nativeGetProtectedContentSupport(); + } + + /** * @hide */ public static class Transaction implements Closeable { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index abefd551331e..32974acdcbbd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -76,8 +76,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.Trace; +import android.sysprop.DisplayProperties; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; @@ -812,14 +812,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture"; /** - * When set to true, apps will draw debugging information about their layouts. - * - * @hide - */ - @UnsupportedAppUsage - public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout"; - - /** * When set to true, this view will save its attribute data. * * @hide @@ -27833,7 +27825,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Show where the margins, bounds and layout bounds are for each view. */ - boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); + boolean mDebugLayout = DisplayProperties.debug_layout().orElse(false); /** * Point used to compute visible regions. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8e4dc670a2d3..27d4ea4a777a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -75,6 +75,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.sysprop.DisplayProperties; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.Log; @@ -7071,7 +7072,7 @@ public final class ViewRootImpl implements ViewParent, } // Layout debugging - boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false); + boolean layout = DisplayProperties.debug_layout().orElse(false); if (layout != mAttachInfo.mDebugLayout) { mAttachInfo.mDebugLayout = layout; if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index c630177e1bf2..dfd9a2e95cb7 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -187,6 +187,7 @@ public abstract class Animation implements Cloneable { /** * An animation listener to be notified when the animation starts, ends or repeats. */ + @UnsupportedAppUsage private AnimationListener mListener; /** diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 93941d0d6edb..888a4c57751e 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2997,5 +2997,23 @@ public final class AutofillManager { afm.post(() -> afm.autofill(sessionId, ids, values)); } } + + @Override + public void requestShowFillUi(int sessionId, AutofillId id, int width, int height, + Rect anchorBounds, IAutofillWindowPresenter presenter) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds, + presenter)); + } + } + + @Override + public void requestHideFillUi(int sessionId, AutofillId id) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.requestHideFillUi(id, false)); + } + } } } diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl index 67cd0bf87b99..140507c80ed0 100644 --- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl +++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl @@ -21,6 +21,7 @@ import java.util.List; import android.graphics.Rect; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import android.view.autofill.IAutofillWindowPresenter; /** * Object running in the application process and responsible to provide the functionalities @@ -29,6 +30,24 @@ import android.view.autofill.AutofillValue; * @hide */ interface IAugmentedAutofillManagerClient { - Rect getViewCoordinates(in AutofillId id); - void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values); + /** + * Gets the coordinates of the input field view. + */ + Rect getViewCoordinates(in AutofillId id); + + /** + * Autofills the activity with the contents of the values. + */ + void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values); + + /** + * Requests showing the fill UI. + */ + void requestShowFillUi(int sessionId, in AutofillId id, int width, int height, + in Rect anchorBounds, in IAutofillWindowPresenter presenter); + + /** + * Requests hiding the fill UI. + */ + void requestHideFillUi(int sessionId, in AutofillId id); } diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index 04e725e64e9c..cb4c4b4b6899 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -68,8 +68,8 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override - void flush() { - mParent.flush(); + void flush(@FlushReason int reason) { + mParent.flush(reason); } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index ff45efd944cd..413f1a5a8955 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -24,17 +24,18 @@ import android.annotation.UiThread; import android.content.ComponentName; import android.content.Context; import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; +import android.view.contentcapture.ContentCaptureSession.FlushReason; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; import com.android.internal.util.Preconditions; import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; -import java.util.concurrent.atomic.AtomicBoolean; /* * NOTE: all methods in this class should return right away, or do the real work in a handler @@ -51,8 +52,6 @@ public final class ContentCaptureManager { private static final String TAG = ContentCaptureManager.class.getSimpleName(); - private static final String BG_THREAD_NAME = "intel_svc_streamer_thread"; - /** * Timeout for calls to system_server. */ @@ -62,8 +61,10 @@ public final class ContentCaptureManager { static final boolean VERBOSE = false; static final boolean DEBUG = true; // STOPSHIP if not set to false - @NonNull - private final AtomicBoolean mDisabled = new AtomicBoolean(); + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private boolean mDisabled; @NonNull private final Context mContext; @@ -71,35 +72,29 @@ public final class ContentCaptureManager { @Nullable private final IContentCaptureManager mService; + // Flags used for starting session. + @GuardedBy("mLock") + private int mFlags; + // TODO(b/119220549): use UI Thread directly (as calls are one-way) or a shared thread / handler // held at the Application level @NonNull private final Handler mHandler; + @GuardedBy("mLock") private MainContentCaptureSession mMainSession; /** @hide */ public ContentCaptureManager(@NonNull Context context, @Nullable IContentCaptureManager service) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); - if (VERBOSE) { - Log.v(TAG, "Constructor for " + context.getPackageName()); - } - mService = service; - // TODO(b/119220549): use an existing bg thread instead... - final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME); - bgThread.start(); - mHandler = Handler.createAsync(bgThread.getLooper()); - } + if (VERBOSE) Log.v(TAG, "Constructor for " + context.getPackageName()); - @NonNull - private static Handler newHandler() { - // TODO(b/119220549): use an existing bg thread instead... - // TODO(b/119220549): use UI Thread directly (as calls are one-way) or an existing bgThread - // or a shared thread / handler held at the Application level - final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME); - bgThread.start(); - return Handler.createAsync(bgThread.getLooper()); + mService = service; + // TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we + // do, then we should optimize it to run the tests after the Choreographer finishes the most + // important steps of the frame. + mHandler = Handler.createAsync(Looper.getMainLooper()); } /** @@ -114,20 +109,25 @@ public final class ContentCaptureManager { @NonNull @UiThread public MainContentCaptureSession getMainContentCaptureSession() { - if (mMainSession == null) { - mMainSession = new MainContentCaptureSession(mContext, mHandler, mService, - mDisabled); - if (VERBOSE) { - Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession); + synchronized (mLock) { + if (mMainSession == null) { + mMainSession = new MainContentCaptureSession(mContext, mHandler, mService, + mDisabled); + if (VERBOSE) { + Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession); + } } + return mMainSession; } - return mMainSession; } /** @hide */ public void onActivityStarted(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent, int flags) { - getMainContentCaptureSession().start(applicationToken, activityComponent, flags); + synchronized (mLock) { + mFlags |= flags; + getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags); + } } /** @hide */ @@ -142,8 +142,8 @@ public final class ContentCaptureManager { * * @hide */ - public void flush() { - getMainContentCaptureSession().flush(); + public void flush(@FlushReason int reason) { + getMainContentCaptureSession().flush(reason); } /** @@ -173,7 +173,9 @@ public final class ContentCaptureManager { * Checks whether content capture is enabled for this activity. */ public boolean isContentCaptureEnabled() { - return mService != null && !mDisabled.get(); + synchronized (mLock) { + return mService != null && !mDisabled; + } } /** @@ -183,7 +185,9 @@ public final class ContentCaptureManager { * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}. */ public void setContentCaptureEnabled(boolean enabled) { - //TODO(b/111276913): implement (need to finish / disable all sessions) + synchronized (mLock) { + mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP; + } } /** @@ -198,20 +202,22 @@ public final class ContentCaptureManager { /** @hide */ public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.println("ContentCaptureManager"); - - pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled.get()); - pw.print(prefix); pw.print("Context: "); pw.println(mContext); - pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId()); - if (mService != null) { - pw.print(prefix); pw.print("Service: "); pw.println(mService); - } - if (mMainSession != null) { - final String prefix2 = prefix + " "; - pw.print(prefix); pw.println("Main session:"); - mMainSession.dump(prefix2, pw); - } else { - pw.print(prefix); pw.println("No sessions"); + synchronized (mLock) { + pw.print(prefix); pw.println("ContentCaptureManager"); + pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); + pw.print(prefix); pw.print("Context: "); pw.println(mContext); + pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId()); + if (mService != null) { + pw.print(prefix); pw.print("Service: "); pw.println(mService); + } + pw.print(prefix); pw.print("Flags: "); pw.println(mFlags); + if (mMainSession != null) { + final String prefix2 = prefix + " "; + pw.print(prefix); pw.println("Main session:"); + mMainSession.dump(prefix2, pw); + } else { + pw.print(prefix); pw.println("No sessions"); + } } } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index d9a8416dcb4d..a9e7b5e5de70 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -19,8 +19,10 @@ import static android.view.contentcapture.ContentCaptureManager.DEBUG; import static android.view.contentcapture.ContentCaptureManager.VERBOSE; import android.annotation.CallSuper; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.DebugUtils; import android.util.Log; import android.view.View; import android.view.ViewStructure; @@ -34,6 +36,8 @@ import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.UUID; @@ -56,45 +60,100 @@ public abstract class ContentCaptureSession implements AutoCloseable { * * @hide */ - public static final int STATE_UNKNOWN = 0; + // NOTE: not prefixed by STATE_ so it's not printed on getStateAsString() + public static final int UNKNWON_STATE = 0x0; /** * Service's startSession() was called, but server didn't confirm it was created yet. * * @hide */ - public static final int STATE_WAITING_FOR_SERVER = 1; + public static final int STATE_WAITING_FOR_SERVER = 0x1; /** * Session is active. * * @hide */ - public static final int STATE_ACTIVE = 2; + public static final int STATE_ACTIVE = 0x2; /** * Session is disabled because there is no service for this user. * * @hide */ - public static final int STATE_DISABLED_NO_SERVICE = 3; + public static final int STATE_DISABLED = 0x4; /** * Session is disabled because its id already existed on server. * * @hide */ - public static final int STATE_DISABLED_DUPLICATED_ID = 4; + public static final int STATE_DUPLICATED_ID = 0x8; + + /** + * Session is disabled because service is not set for user. + * + * @hide + */ + public static final int STATE_NO_SERVICE = 0x10; /** * Session is disabled by FLAG_SECURE * * @hide */ - public static final int STATE_DISABLED_BY_FLAG_SECURE = 5; + public static final int STATE_FLAG_SECURE = 0x20; + + /** + * Session is disabled manually by the specific app. + * + * @hide + */ + public static final int STATE_BY_APP = 0x40; + + /** + * Session is disabled because session start was never replied. + * + * @hide + */ + public static final int STATE_NO_RESPONSE = 0x80; + + /** + * Session is disabled because an internal error. + * + * @hide + */ + public static final int STATE_INTERNAL_ERROR = 0x100; private static final int INITIAL_CHILDREN_CAPACITY = 5; + /** @hide */ + public static final int FLUSH_REASON_FULL = 1; + /** @hide */ + public static final int FLUSH_REASON_ACTIVITY_PAUSED = 2; + /** @hide */ + public static final int FLUSH_REASON_ACTIVITY_RESUMED = 3; + /** @hide */ + public static final int FLUSH_REASON_SESSION_STARTED = 4; + /** @hide */ + public static final int FLUSH_REASON_SESSION_FINISHED = 5; + /** @hide */ + public static final int FLUSH_REASON_IDLE_TIMEOUT = 6; + + /** @hide */ + @IntDef(prefix = { "FLUSH_REASON_" }, value = { + FLUSH_REASON_FULL, + FLUSH_REASON_ACTIVITY_PAUSED, + FLUSH_REASON_ACTIVITY_RESUMED, + FLUSH_REASON_SESSION_STARTED, + FLUSH_REASON_SESSION_FINISHED, + FLUSH_REASON_IDLE_TIMEOUT + }) + @Retention(RetentionPolicy.SOURCE) + @interface FlushReason{} + + private final CloseGuard mCloseGuard = CloseGuard.get(); private final Object mLock = new Object(); @@ -110,7 +169,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { @Nullable protected final String mId; - private int mState = STATE_UNKNOWN; + private int mState = UNKNWON_STATE; // Lazily created on demand. private ContentCaptureSessionId mContentCaptureSessionId; @@ -182,7 +241,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** * Flushes the buffered events to the service. */ - abstract void flush(); + abstract void flush(@FlushReason int reason); /** * Destroys this session, flushing out all pending notifications to the service. @@ -220,7 +279,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { } try { - flush(); + flush(FLUSH_REASON_SESSION_FINISHED); } finally { onDestroy(); } @@ -377,26 +436,31 @@ public abstract class ContentCaptureSession implements AutoCloseable { return mId; } - /** - * @hide - */ + /** @hide */ @NonNull protected static String getStateAsString(int state) { - switch (state) { - case STATE_UNKNOWN: - return "UNKNOWN"; - case STATE_WAITING_FOR_SERVER: - return "WAITING_FOR_SERVER"; - case STATE_ACTIVE: - return "ACTIVE"; - case STATE_DISABLED_NO_SERVICE: - return "DISABLED_NO_SERVICE"; - case STATE_DISABLED_DUPLICATED_ID: - return "DISABLED_DUPLICATED_ID"; - case STATE_DISABLED_BY_FLAG_SECURE: - return "DISABLED_FLAG_SECURE"; + return state + " (" + (state == UNKNWON_STATE ? "UNKNOWN" + : DebugUtils.flagsToString(ContentCaptureSession.class, "STATE_", state)) + ")"; + } + + /** @hide */ + @NonNull + static String getflushReasonAsString(@FlushReason int reason) { + switch (reason) { + case FLUSH_REASON_FULL: + return "FULL"; + case FLUSH_REASON_ACTIVITY_PAUSED: + return "PAUSED"; + case FLUSH_REASON_ACTIVITY_RESUMED: + return "RESUMED"; + case FLUSH_REASON_SESSION_STARTED: + return "STARTED"; + case FLUSH_REASON_SESSION_FINISHED: + return "FINISHED"; + case FLUSH_REASON_IDLE_TIMEOUT: + return "IDLE"; default: - return "INVALID:" + state; + return "UNKOWN-" + reason; } } } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index a29aaf013d49..f0778fa343b1 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -35,7 +35,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; -import android.os.SystemClock; +import android.util.LocalLog; import android.util.Log; import android.util.TimeUtils; import android.view.autofill.AutofillId; @@ -88,6 +88,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { */ public static final String EXTRA_BINDER = "binder"; + // TODO(b/111276913): make sure disabled state is in sync with manager's disabled @NonNull private final AtomicBoolean mDisabled; @@ -113,7 +114,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Nullable private DeathRecipient mDirectServiceVulture; - private int mState = STATE_UNKNOWN; + private int mState = UNKNWON_STATE; @Nullable private IBinder mApplicationToken; @@ -130,14 +131,17 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // Used just for debugging purposes (on dump) private long mNextFlush; + // TODO(b/121044064): use settings to set size + private final LocalLog mFlushHistory = new LocalLog(10); + /** @hide */ protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler, @Nullable IContentCaptureManager systemServerInterface, - @NonNull AtomicBoolean disabled) { + @NonNull boolean disabled) { mContext = context; mHandler = handler; mSystemServerInterface = systemServerInterface; - mDisabled = disabled; + mDisabled = new AtomicBoolean(disabled); } @Override @@ -171,8 +175,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override - void flush() { - mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this)); + void flush(@FlushReason int reason) { + mHandler.sendMessage( + obtainMessage(MainContentCaptureSession::handleForceFlush, this, reason)); } @Override @@ -184,10 +189,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName, int flags) { - if (mState != STATE_UNKNOWN) { - // TODO(b/111276913): revisit this scenario - Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state " - + getStateAsString(mState)); + if (handleHasStarted()) { + // TODO(b/122959591): make sure this is expected (and when), or use Log.w + if (DEBUG) { + Log.d(TAG, "ignoring handleStartSession(" + token + "/" + + ComponentName.flattenToShortString(componentName) + " while on state " + + getStateAsString(mState)); + } return; } mState = STATE_WAITING_FOR_SERVER; @@ -196,7 +204,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (VERBOSE) { Log.v(TAG, "handleStartSession(): token=" + token + ", act=" - + getActivityDebugName() + ", id=" + mId); + + getDebugState() + ", id=" + mId); } try { @@ -211,7 +219,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { binder = resultData.getBinder(EXTRA_BINDER); if (binder == null) { Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result"); - handleResetState(); + handleResetSession(STATE_DISABLED | STATE_INTERNAL_ERROR); return; } } @@ -233,7 +241,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { * @param binder handle to {@code IContentCaptureDirectManager} */ private void handleSessionStarted(int resultCode, @Nullable IBinder binder) { - mState = resultCode; if (binder != null) { mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder); mDirectServiceVulture = () -> { @@ -247,31 +254,39 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } - // TODO(b/111276913): change the resultCode to use flags so there's just one flag for - // disabled stuff - if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID - || resultCode == STATE_DISABLED_BY_FLAG_SECURE) { - mDisabled.set(true); - handleResetSession(/* resetState= */ false); + if ((resultCode & STATE_DISABLED) != 0) { + handleResetSession(resultCode); } else { + mState = resultCode; mDisabled.set(false); } if (VERBOSE) { - Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId + Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get() + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size())); } } private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) { + final int eventType = event.getType(); + if (!handleHasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) { + // TODO(b/120494182): comment when this could happen (dialogs?) + Log.v(TAG, "handleSendEvent(" + getDebugState() + ", " + + ContentCaptureEvent.getTypeAsString(eventType) + + "): session not started yet"); + return; + } + if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event); if (mEvents == null) { if (VERBOSE) { - Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events"); + Log.v(TAG, "handleSendEvent(" + getDebugState() + ", " + + ContentCaptureEvent.getTypeAsString(eventType) + + "): creating buffer for " + MAX_BUFFER_SIZE + " events"); } mEvents = new ArrayList<>(MAX_BUFFER_SIZE); } - if (!mEvents.isEmpty() && event.getType() == TYPE_VIEW_TEXT_CHANGED) { + if (!mEvents.isEmpty() && eventType == TYPE_VIEW_TEXT_CHANGED) { final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1); // TODO(b/121045053): check if flags match @@ -294,76 +309,104 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE; if (bufferEvent && !forceFlush) { - handleScheduleFlush(/* checkExisting= */ true); + handleScheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true); return; } if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) { // Callback from startSession hasn't been called yet - typically happens on system // apps that are started before the system service - // TODO(b/111276913): try to ignore session while system is not ready / boot + // TODO(b/122959591): try to ignore session while system is not ready / boot // not complete instead. Similarly, the manager service should return right away // when the user does not have a service set if (DEBUG) { - Log.d(TAG, "Closing session for " + getActivityDebugName() - + " after " + numberEvents + " delayed events and state " - + getStateAsString(mState)); + Log.d(TAG, "Closing session for " + getDebugState() + + " after " + numberEvents + " delayed events"); } - handleResetState(); + handleResetSession(STATE_DISABLED | STATE_NO_RESPONSE); // TODO(b/111276913): blacklist activity / use special flag to indicate that // when it's launched again return; } + final int flushReason; + switch (eventType) { + case ContentCaptureEvent.TYPE_SESSION_STARTED: + flushReason = FLUSH_REASON_SESSION_STARTED; + break; + case ContentCaptureEvent.TYPE_SESSION_FINISHED: + flushReason = FLUSH_REASON_SESSION_FINISHED; + break; + default: + flushReason = FLUSH_REASON_FULL; + } - handleForceFlush(); + handleForceFlush(flushReason); } - private void handleScheduleFlush(boolean checkExisting) { + private boolean handleHasStarted() { + return mState != UNKNWON_STATE; + } + + private void handleScheduleFlush(@FlushReason int reason, boolean checkExisting) { + if (!handleHasStarted()) { + Log.v(TAG, "handleScheduleFlush(" + getDebugState() + "): session not started yet"); + return; + } if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) { // "Renew" the flush message by removing the previous one mHandler.removeMessages(MSG_FLUSH); } - mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS; + mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS; if (VERBOSE) { - Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush); + Log.v(TAG, "handleScheduleFlush(" + getDebugState() + + ", reason=" + getflushReasonAsString(reason) + "): scheduled to flush in " + + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush)); } mHandler.sendMessageDelayed( - obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this) + obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this, reason) .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS); } - private void handleFlushIfNeeded() { + private void handleFlushIfNeeded(@FlushReason int reason) { if (mEvents.isEmpty()) { if (VERBOSE) Log.v(TAG, "Nothing to flush"); return; } - handleForceFlush(); + handleForceFlush(reason); } - private void handleForceFlush() { + private void handleForceFlush(@FlushReason int reason) { if (mEvents == null) return; if (mDirectServiceInterface == null) { if (VERBOSE) { - Log.v(TAG, "handleForceFlush(): hold your horses, client not ready: " + mEvents); + Log.v(TAG, "handleForceFlush(" + getDebugState() + + ", reason=" + getflushReasonAsString(reason) + + "): hold your horses, client not ready: " + mEvents); } if (!mHandler.hasMessages(MSG_FLUSH)) { - handleScheduleFlush(/* checkExisting= */ false); + handleScheduleFlush(reason, /* checkExisting= */ false); } return; } final int numberEvents = mEvents.size(); + final String reasonString = getflushReasonAsString(reason); + if (DEBUG) { + Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState() + + ". Reason: " + reasonString); + } + // Logs reason, size, max size, idle timeout + final String logRecord = "r=" + reasonString + " s=" + numberEvents + + " m=" + MAX_BUFFER_SIZE + " i=" + FLUSHING_FREQUENCY_MS; try { - if (DEBUG) { - Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName()); - } + mFlushHistory.log(logRecord); mHandler.removeMessages(MSG_FLUSH); final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents(); mDirectServiceInterface.sendEvents(events); } catch (RemoteException e) { - Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName() + Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState() + ": " + e); } } @@ -386,7 +429,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (DEBUG) { Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with " + (mEvents == null ? 0 : mEvents.size()) + " event(s) for " - + getActivityDebugName()); + + getDebugState()); } try { @@ -395,21 +438,19 @@ public final class MainContentCaptureSession extends ContentCaptureSession { mSystemServerInterface.finishSession(mContext.getUserId(), mId); } catch (RemoteException e) { Log.e(TAG, "Error destroying system-service session " + mId + " for " - + getActivityDebugName() + ": " + e); + + getDebugState() + ": " + e); } } - private void handleResetState() { - handleResetSession(/* resetState= */ true); - } - // TODO(b/122454205): once we support multiple sessions, we might need to move some of these // clearings out. - private void handleResetSession(boolean resetState) { - if (resetState) { - mState = STATE_UNKNOWN; + private void handleResetSession(int newState) { + if (VERBOSE) { + Log.v(TAG, "handleResetSession(" + getActivityName() + "): from " + + getStateAsString(mState) + " to " + getStateAsString(newState)); } - + mState = newState; + mDisabled.set((newState & STATE_DISABLED) != 0); // TODO(b/122454205): must reset children (which currently is owned by superclass) mApplicationToken = null; mComponentName = null; @@ -483,7 +524,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { - pw.print(prefix); pw.print("id: "); pw.println(mId); pw.print(prefix); pw.print("mContext: "); pw.println(mContext); pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId()); if (mSystemServerInterface != null) { @@ -496,8 +536,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get()); pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled()); - pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" ("); - pw.print(getStateAsString(mState)); pw.println(")"); + pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState)); if (mApplicationToken != null) { pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken); } @@ -519,16 +558,25 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS); pw.print(prefix); pw.print("next flush: "); - TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println(); + TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw); + pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")"); } + pw.print(prefix); pw.println("flush history:"); + mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println(); + super.dump(prefix, pw); } /** * Gets a string that can be used to identify the activity on logging statements. */ - private String getActivityDebugName() { - return mComponentName == null ? mContext.getPackageName() - : mComponentName.flattenToShortString(); + private String getActivityName() { + return mComponentName == null + ? "pkg:" + mContext.getPackageName() + : "act:" + mComponentName.flattenToShortString(); + } + + private String getDebugState() { + return getActivityName() + " (state=" + getStateAsString(mState) + ")"; } } diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java index 5b95715681fc..e2a763e22288 100644 --- a/core/java/android/view/inspector/InspectableProperty.java +++ b/core/java/android/view/inspector/InspectableProperty.java @@ -105,7 +105,6 @@ public @interface InspectableProperty { /** * One entry in an enumeration packed into a primitive {int}. * - * @see IntEnumMapping * @hide */ @Target({TYPE}) diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java deleted file mode 100644 index 69f6dce94a81..000000000000 --- a/core/java/android/view/inspector/IntEnumMapping.java +++ /dev/null @@ -1,118 +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.view.inspector; - -import android.annotation.NonNull; -import android.annotation.Nullable; - -import java.util.ArrayList; - -/** - * Maps the values of an {int} property to string names for properties that encode enumerations. - * - * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper} - * for enumerations packed into primitive {int} properties. - * - * This class is immutable, and must be constructed by a {@link Builder}. - * - * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping) - */ -public final class IntEnumMapping { - private final Value[] mValues; - - /** - * Map from a property value to a string name. - * - * @param value The value of a property - * @return The name of the enumeration value, null if the value is not mapped - */ - @Nullable - public String nameOf(int value) { - for (Value valueTuple : mValues) { - if (valueTuple.mValue == value) { - return valueTuple.mName; - } - } - - return null; - } - - /** - * Create a new instance from a builder. - * - * This constructor is private, use {@link Builder#build()} instead. - * - * @param builder A builder to create from - */ - private IntEnumMapping(Builder builder) { - mValues = builder.mValues.toArray(new Value[builder.mValues.size()]); - } - - /** - * A builder for {@link IntEnumMapping} - */ - public static final class Builder { - private final ArrayList<Value> mValues; - - public Builder() { - mValues = new ArrayList<>(); - } - - /** - * Add a new entry to this mapping. - * - * @param name Name of the enumeration value - * @param value Int value of the enumeration value - * @return This builder - */ - @NonNull - public Builder addValue(@NonNull String name, int value) { - mValues.add(new Value(name, value)); - return this; - } - - /** - * Clear the builder, allowing for recycling. - */ - public void clear() { - mValues.clear(); - } - - /** - * Build a new {@link IntEnumMapping} from this builder - * - * @return A new mapping - */ - @NonNull - public IntEnumMapping build() { - return new IntEnumMapping(this); - } - } - - /** - * Inner class that holds the name and value of an enumeration value. - */ - private static final class Value { - @NonNull private final String mName; - private final int mValue; - - private Value(@NonNull String name, int value) { - mName = name; - mValue = value; - } - } -} diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java index dcb87e18ae5e..8f7dfd5c5144 100644 --- a/core/java/android/view/inspector/IntFlagMapping.java +++ b/core/java/android/view/inspector/IntFlagMapping.java @@ -19,14 +19,20 @@ package android.view.inspector; import android.annotation.NonNull; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; /** - * Maps the values of an {int} property to arrays of string for properties that encode flags. + * Maps the values of an {@code int} property to arrays of string for properties that encode flags. * * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper} - * for flag values packed into primitive {int} properties. + * for flag values packed into primitive {@code int} properties. * - * Each flag has a + * Each flag has a mask and a target value, for non-exclusive flags, the target can also be used as + * the mask. A given integer value is compared against each flag to find what flags are active for + * it by bitwise anding it with the mask and comparing the result against the target, that is, + * {@code (value & mask) == target}. * * This class is immutable, and must be constructed by a {@link Builder}. * @@ -42,8 +48,8 @@ public final class IntFlagMapping { * @return The names of the enabled flags */ @NonNull - public String[] namesOf(int value) { - ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length); + public Set<String> get(int value) { + final Set<String> enabledFlagNames = new HashSet<>(mFlags.length); for (Flag flag : mFlags) { if (flag.isEnabledFor(value)) { @@ -51,7 +57,7 @@ public final class IntFlagMapping { } } - return enabledFlagNames.toArray(new String[enabledFlagNames.size()]); + return Collections.unmodifiableSet(enabledFlagNames); } /** @@ -81,7 +87,7 @@ public final class IntFlagMapping { * The target value will be used as a mask, to handle the common case where flag values * are not mutually exclusive. The flag will be considered enabled for a property value if * the result of bitwise anding the target and the value equals the target, that is: - * {(value & target) == target}. + * {@code (value & target) == target}. * * @param name The name of the flag * @param target The value to compare against @@ -97,7 +103,7 @@ public final class IntFlagMapping { * Add a new flag with a mask. * * The flag will be considered enabled for a property value if the result of bitwise anding - * the value and the mask equals the target, that is: {(value & mask) == target}. + * the value and the mask equals the target, that is: {@code (value & mask) == target}. * * @param name The name of the flag * @param target The value to compare against @@ -111,13 +117,6 @@ public final class IntFlagMapping { } /** - * Clear the builder, allowing for recycling. - */ - public void clear() { - mFlags.clear(); - } - - /** * Build a new {@link IntFlagMapping} from this builder. * * @return A new mapping @@ -143,7 +142,7 @@ public final class IntFlagMapping { } /** - * Compare the supplied property value against the mask and taget. + * Compare the supplied property value against the mask and target. * * @param value The value to check * @return True if this flag is enabled diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java index 5fb291b34219..e20582bf3ee4 100644 --- a/core/java/android/view/inspector/PropertyMapper.java +++ b/core/java/android/view/inspector/PropertyMapper.java @@ -18,6 +18,7 @@ package android.view.inspector; import android.annotation.AttrRes; import android.annotation.NonNull; +import android.util.SparseArray; /** * An interface for mapping the string names of inspectable properties to integer identifiers. @@ -154,7 +155,7 @@ public interface PropertyMapper { int mapIntEnum( @NonNull String name, @AttrRes int attributeId, - @NonNull IntEnumMapping mapping); + @NonNull SparseArray<String> mapping); /** * Map a string name to an integer ID for a flag set packed into an int property. diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java index fd83e8df6c3a..a8b7ecc3f57e 100644 --- a/core/java/android/view/inspector/PropertyReader.java +++ b/core/java/android/view/inspector/PropertyReader.java @@ -25,7 +25,13 @@ import android.graphics.Color; /** * An interface for reading the properties of an inspectable object. * - * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}. + * {@code PropertyReader} is defined as an interface that will be called by + * {@link InspectionCompanion#readProperties(Object, PropertyReader)}. This approach allows a + * client inspector to read the values of primitive properties without the overhead of + * instantiating a class to hold the property values for each inspection pass. If an inspectable + * remains unchanged between reading passes, it should be possible for a {@code PropertyReader} to + * avoid new allocations for subsequent reading passes. + * * It has separate methods for all primitive types to avoid autoboxing overhead if a concrete * implementation is able to work with primitives. Implementations should be prepared to accept * {null} as the value of {@link PropertyReader#readObject(int, Object)}. @@ -38,7 +44,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {boolean} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code boolean} */ void readBoolean(int id, boolean value); @@ -47,7 +53,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {byte} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code byte} */ void readByte(int id, byte value); @@ -56,7 +62,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {char} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code char} */ void readChar(int id, char value); @@ -65,7 +71,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {double} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code double} */ void readDouble(int id, double value); @@ -74,7 +80,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {float} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code float} */ void readFloat(int id, float value); @@ -83,7 +89,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as an {int} + * @throws PropertyTypeMismatchException If the property ID is not mapped as an {@code int} */ void readInt(int id, int value); @@ -92,7 +98,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {long} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code long} */ void readLong(int id, long value); @@ -101,7 +107,7 @@ public interface PropertyReader { * * @param id Identifier of the property from a {@link PropertyMapper} * @param value Value of the property - * @throws PropertyTypeMismatchException If the property ID is not mapped as a {short} + * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code short} */ void readShort(int id, short value); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index bad2dbfebdc5..60393502bbe7 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -16,6 +16,7 @@ package android.webkit; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -69,6 +70,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; /** * A View that displays web pages. @@ -1688,6 +1690,84 @@ public class WebView extends AbsoluteLayout return mProvider.getWebViewClient(); } + + /** + * Gets the WebView renderer associated with this WebView. + * + * <p>In {@link android.os.Build.VERSION_CODES#O} and above, WebView may + * run in "multiprocess" mode. In multiprocess mode, rendering of web + * content is performed by a sandboxed renderer process separate to the + * application process. This renderer process may be shared with other + * WebViews in the application, but is not shared with other application + * processes. + * + * <p>If WebView is running in multiprocess mode, this method returns a + * handle to the renderer process associated with the WebView, which can + * be used to control the renderer process. + * + * @return the {@link WebViewRenderer} renderer handle associated + * with this {@link WebView}, or {@code null} if + * WebView is not runing in multiprocess mode. + */ + @Nullable + public WebViewRenderer getWebViewRenderer() { + checkThread(); + return mProvider.getWebViewRenderer(); + } + + /** + * Sets the renderer client object associated with this WebView. + * + * <p>The renderer client encapsulates callbacks relevant to WebView renderer + * state. See {@link WebViewRendererClient} for details. + * + * <p>Although many WebView instances may share a single underlying + * renderer, and renderers may live either in the application + * process, or in a sandboxed process that is isolated from the + * application process, instances of {@link WebViewRendererClient} + * are set per-WebView. Callbacks represent renderer events from + * the perspective of this WebView, and may or may not be correlated + * with renderer events affecting other WebViews. + * + * @param executor the Executor on which {@link WebViewRendererClient} callbacks will execute. + * @param webViewRendererClient the {@link WebViewRendererClient} object. + */ + public void setWebViewRendererClient( + @NonNull @CallbackExecutor Executor executor, + @NonNull WebViewRendererClient webViewRendererClient) { + checkThread(); + mProvider.setWebViewRendererClient(executor, webViewRendererClient); + } + + /** + * Sets the renderer client object associated with this WebView. + * + * See {@link #setWebViewRendererClient(Executor,WebViewRendererClient)} for details. + * + * <p> {@link WebViewRendererClient} callbacks will run on the thread that this WebView was + * initialized on. + * + * @param webViewRendererClient the {@link WebViewRendererClient} object. + */ + public void setWebViewRendererClient( + @Nullable WebViewRendererClient webViewRendererClient) { + checkThread(); + mProvider.setWebViewRendererClient(null, webViewRendererClient); + } + + /** + * Gets the renderer client object associated with this WebView. + * + * @return the {@link WebViewRendererClient} object associated with this WebView, if one has + * been set via {@link #setWebViewRendererClient(WebViewRendererClient)} or {@code null} + * otherwise. + */ + @Nullable + public WebViewRendererClient getWebViewRendererClient() { + checkThread(); + return mProvider.getWebViewRendererClient(); + } + /** * Registers the interface to be used when content can not be handled by * the rendering engine, and should be downloaded instead. This will replace diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index 95e7a986efd2..baf582693bd8 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -54,6 +54,7 @@ import android.webkit.WebView.VisualStateCallback; import java.io.BufferedWriter; import java.io.File; import java.util.Map; +import java.util.concurrent.Executor; /** * WebView backend provider interface: this interface is the abstract backend to a WebView @@ -237,6 +238,14 @@ public interface WebViewProvider { public WebViewClient getWebViewClient(); + public WebViewRenderer getWebViewRenderer(); + + public void setWebViewRendererClient( + @Nullable Executor executor, + @Nullable WebViewRendererClient client); + + public WebViewRendererClient getWebViewRendererClient(); + public void setDownloadListener(DownloadListener listener); public void setWebChromeClient(WebChromeClient client); diff --git a/core/java/android/webkit/WebViewRenderer.java b/core/java/android/webkit/WebViewRenderer.java new file mode 100644 index 000000000000..532825485ed3 --- /dev/null +++ b/core/java/android/webkit/WebViewRenderer.java @@ -0,0 +1,45 @@ +/* + * 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.webkit; + +/** + * WebViewRenderer provides an opaque handle to a {@link WebView} renderer. + */ +public abstract class WebViewRenderer { + /** + * Cause this renderer to terminate. + * + * <p>Calling this on a not yet started, or an already terminated renderer will have no effect. + * + * <p>Terminating a renderer process may have an effect on multiple {@link WebView} instances. + * + * <p>Renderer termination must be handled by properly overriding + * {@link WebViewClient#onRenderProcessGone} for every WebView that shares this + * renderer. If termination is not handled by all associated WebViews, then the application + * process will also be terminated. + * + * @return {@code true} if it was possible to terminate this renderer, {@code false} otherwise. + */ + public abstract boolean terminate(); + + /** + * This class cannot be created by applications. + * @hide + */ + public WebViewRenderer() { + } +} diff --git a/core/java/android/webkit/WebViewRendererClient.java b/core/java/android/webkit/WebViewRendererClient.java new file mode 100644 index 000000000000..2fadf54fd434 --- /dev/null +++ b/core/java/android/webkit/WebViewRendererClient.java @@ -0,0 +1,77 @@ +/* + * 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.webkit; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * Used to receive callbacks on {@link WebView} renderer events. + * + * WebViewRendererClient instances may be set or retrieved via {@link + * WebView#setWebViewRendererClient(WebViewRendererClient)} and {@link + * WebView#getWebViewRendererClient()}. + * + * Instances may be attached to multiple WebViews, and thus a single renderer event may cause + * a callback to be called multiple times with different WebView parameters. + */ +public abstract class WebViewRendererClient { + /** + * Called when the renderer currently associated with {@code view} becomes unresponsive as a + * result of a long running blocking task such as the execution of JavaScript. + * + * <p>If a WebView fails to process an input event, or successfully navigate to a new URL within + * a reasonable time frame, the renderer is considered to be unresponsive, and this callback + * will be called. + * + * <p>This callback will continue to be called at regular intervals as long as the renderer + * remains unresponsive. If the renderer becomes responsive again, {@link + * WebViewRendererClient#onRendererResponsive} will be called once, and this method will not + * subsequently be called unless another period of unresponsiveness is detected. + * + * <p>No action is taken by WebView as a result of this method call. Applications may + * choose to terminate the associated renderer via the object that is passed to this callback, + * if in multiprocess mode, however this must be accompanied by correctly handling + * {@link WebViewClient#onRenderProcessGone} for this WebView, and all other WebViews associated + * with the same renderer. Failure to do so will result in application termination. + * + * @param view The {@link WebView} for which unresponsiveness was detected. + * @param renderer The {@link WebViewRenderer} that has become unresponsive, + * or {@code null} if WebView is running in single process mode. + */ + public abstract void onRendererUnresponsive( + @NonNull WebView view, @Nullable WebViewRenderer renderer); + + /** + * Called once when an unresponsive renderer currently associated with {@code view} becomes + * responsive. + * + * <p>After a WebView renderer becomes unresponsive, which is notified to the application by + * {@link WebViewRendererClient#onRendererUnresponsive}, it is possible for the blocking + * renderer task to complete, returning the renderer to a responsive state. In that case, + * this method is called once to indicate responsiveness. + * + * <p>No action is taken by WebView as a result of this method call. + * + * @param view The {@link WebView} for which responsiveness was detected. + * + * @param renderer The {@link WebViewRenderer} that has become responsive, or {@code null} if + * WebView is running in single process mode. + */ + public abstract void onRendererResponsive( + @NonNull WebView view, @Nullable WebViewRenderer renderer); +} diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java index 2995a8f43268..7a00a51647f6 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -58,6 +58,15 @@ public class AmbientDisplayConfiguration { return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup); } + public boolean tapGestureEnabled(int user) { + return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user) + && tapSensorAvailable(); + } + + public boolean tapSensorAvailable() { + return !TextUtils.isEmpty(tapSensorType()); + } + public boolean doubleTapGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, user) && doubleTapSensorAvailable(); @@ -86,6 +95,10 @@ public class AmbientDisplayConfiguration { return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType); } + public String tapSensorType() { + return mContext.getResources().getString(R.string.config_dozeTapSensorType); + } + public String longPressSensorType() { return mContext.getResources().getString(R.string.config_dozeLongPressSensorType); } diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java index 594595843ca7..72c67d7ddc08 100644 --- a/core/java/com/android/internal/infra/AbstractRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractRemoteService.java @@ -139,6 +139,14 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I return mDestroyed; } + /** + * Gets the name of the service. + */ + @NonNull + public final ComponentName getComponentName() { + return mComponentName; + } + private void handleOnConnectedStateChangedInternal(boolean connected) { if (connected) { handlePendingRequests(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index cc8da5c6317f..17cc6afcd8f0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -30,6 +30,7 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; +import android.net.INetworkStatsService; import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiActivityEnergyInfo; @@ -86,7 +87,6 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.location.gnssmetrics.GnssMetrics; -import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -337,6 +337,25 @@ public class BatteryStatsImpl extends BatteryStats { private final PlatformIdleStateCallback mPlatformIdleStateCallback; + private final Runnable mDeferSetCharging = new Runnable() { + @Override + public void run() { + synchronized (BatteryStatsImpl.this) { + if (mOnBattery) { + // if the device gets unplugged in the time between this runnable being + // executed and the lock being taken, we don't want to set charging state + return; + } + boolean changed = setChargingLocked(true); + if (changed) { + final long uptime = mClocks.uptimeMillis(); + final long elapsedRealtime = mClocks.elapsedRealtime(); + addHistoryRecordLocked(elapsedRealtime, uptime); + } + } + } + }; + /** * This handler is running on {@link BackgroundThread}. */ @@ -11012,7 +11031,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory(); private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6); private final Object mWifiNetworkLock = new Object(); @@ -11034,11 +11052,16 @@ public class BatteryStatsImpl extends BatteryStats { private NetworkStats readNetworkStatsLocked(String[] ifaces) { try { if (!ArrayUtils.isEmpty(ifaces)) { - return mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, - NetworkStats.TAG_NONE, mNetworkStatsPool.acquire()); + INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + if (statsService != null) { + return statsService.getDetailedUidStats(ifaces); + } else { + Slog.e(TAG, "Failed to get networkStatsService "); + } } - } catch (IOException e) { - Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces)); + } catch (RemoteException e) { + Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e); } return null; } @@ -12336,6 +12359,14 @@ public class BatteryStatsImpl extends BatteryStats { } boolean setChargingLocked(boolean charging) { + // if the device is no longer charging, remove the callback + // if the device is now charging, it means that this is either called + // 1. directly when level >= 90 + // 2. or from within the runnable that we deferred + // For 1. if we have an existing callback, remove it, since we will immediatelly send a + // ACTION_CHARGING + // For 2. we remove existing callback so we don't send multiple ACTION_CHARGING + mHandler.removeCallbacks(mDeferSetCharging); if (mCharging != charging) { mCharging = charging; if (charging) { @@ -12674,12 +12705,23 @@ public class BatteryStatsImpl extends BatteryStats { // charging even if it happens to go down a level. changed |= setChargingLocked(true); mLastChargeStepLevel = level; - } if (!mCharging) { + } + if (!mCharging) { if (mLastChargeStepLevel < level) { - // We have not reporting that we are charging, but the level has now - // gone up, so consider the state to be charging. - changed |= setChargingLocked(true); + // We have not reported that we are charging, but the level has gone up, + // but we would like to not have tons of activity from charging-constraint + // jobs, so instead of reporting ACTION_CHARGING immediately, we defer it. mLastChargeStepLevel = level; + if (!mHandler.hasCallbacks(mDeferSetCharging)) { + mHandler.postDelayed( + mDeferSetCharging, + mConstants.BATTERY_CHARGED_DELAY_MS); + } + } else if (mLastChargeStepLevel > level) { + // if we had deferred a runnable due to charge level increasing, but then + // later the charge level drops (could be due to thermal issues), we don't + // want to trigger the deferred runnable, so remove it here + mHandler.removeCallbacks(mDeferSetCharging); } } else { if (mLastChargeStepLevel > level) { @@ -13296,6 +13338,8 @@ public class BatteryStatsImpl extends BatteryStats { = "battery_level_collection_delay_ms"; public static final String KEY_MAX_HISTORY_FILES = "max_history_files"; public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb"; + public static final String KEY_BATTERY_CHARGED_DELAY_MS = + "battery_charged_delay_ms"; private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; @@ -13308,6 +13352,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/ private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64; private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/ + private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */ public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; @@ -13320,6 +13365,7 @@ public class BatteryStatsImpl extends BatteryStats { = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS; public int MAX_HISTORY_FILES; public int MAX_HISTORY_BUFFER; /*Bytes*/ + public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -13388,6 +13434,9 @@ public class BatteryStatsImpl extends BatteryStats { DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB : DEFAULT_MAX_HISTORY_BUFFER_KB) * 1024; + BATTERY_CHARGED_DELAY_MS = mParser.getInt( + KEY_BATTERY_CHARGED_DELAY_MS, + DEFAULT_BATTERY_CHARGED_DELAY_MS); } } @@ -13445,6 +13494,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(MAX_HISTORY_FILES); pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("="); pw.println(MAX_HISTORY_BUFFER/1024); + pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("="); + pw.println(BATTERY_CHARGED_DELAY_MS); } } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index e97c9bc911dd..bc49771c380b 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -557,6 +557,38 @@ namespace PaintGlue { return result; } + // FIXME: Should this be FastNative? + static void setColorLong(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace, + jfloat r, jfloat g, jfloat b, jfloat a) { + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace); + if (GraphicsJNI::hasException(env)) { + return; + } + + SkColor4f color = SkColor4f{r, g, b, a}; + reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get()); + } + + static void setShadowLayerLong(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius, + jfloat dx, jfloat dy, jobject jColorSpace, + jfloat r, jfloat g, jfloat b, jfloat a) { + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace); + if (GraphicsJNI::hasException(env)) { + return; + } + + SkColor4f color = SkColor4f{r, g, b, a}; + + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + if (radius <= 0) { + paint->setLooper(nullptr); + } + else { + SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); + paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy)); + } + } + // ------------------ @FastNative --------------------------- static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) { @@ -1075,6 +1107,9 @@ static const JNINativeMethod methods[] = { {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I}, + {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColorLong}, + {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V", + (void*)PaintGlue::setShadowLayerLong}, // --------------- @FastNative ---------------------- diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 49a24a30f77e..b2d3651079e1 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -281,15 +281,18 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName std::unique_ptr<NativeCode> code; bool needs_native_bridge = false; + char* nativeloader_error_msg = nullptr; void* handle = OpenNativeLibrary(env, sdkVersion, pathStr.c_str(), classLoader, libraryPath, &needs_native_bridge, - &g_error_msg); + &nativeloader_error_msg); if (handle == nullptr) { + g_error_msg = nativeloader_error_msg; + NativeLoaderFreeErrorMessage(nativeloader_error_msg); ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s", pathStr.c_str(), g_error_msg.c_str()); diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 12a8343b4a5c..67c306413bb2 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -841,6 +841,43 @@ static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env, return jStatus; } +static int android_media_AudioRecord_set_microphone_direction(JNIEnv *env, jobject thiz, + jint direction) { + sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for setMicrophoneDirection()"); + return (jint)AUDIO_JAVA_ERROR; + } + + jint jStatus = AUDIO_JAVA_SUCCESS; + status_t status = + lpRecorder->setMicrophoneDirection(static_cast<audio_microphone_direction_t>(direction)); + if (status != NO_ERROR) { + jStatus = nativeToJavaStatus(status); + } + + return jStatus; +} + +static int android_media_AudioRecord_set_microphone_field_dimension(JNIEnv *env, jobject thiz, + jfloat zoom) { + sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for setMicrophoneFieldDimension()"); + return (jint)AUDIO_JAVA_ERROR; + } + + jint jStatus = AUDIO_JAVA_SUCCESS; + status_t status = lpRecorder->setMicrophoneFieldDimension(zoom); + if (status != NO_ERROR) { + jStatus = nativeToJavaStatus(status); + } + + return jStatus; +} + // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) { sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); @@ -896,6 +933,10 @@ static const JNINativeMethod gMethods[] = { {"native_get_active_microphones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioRecord_get_active_microphones}, {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id}, + {"native_set_microphone_direction", "(I)I", + (void *)android_media_AudioRecord_set_microphone_direction}, + {"native_set_microphone_field_dimension", "(F)I", + (void *)android_media_AudioRecord_set_microphone_field_dimension}, }; // field names found in android/media/AudioRecord.java diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 10da89227f51..aa10a2f98a7e 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -138,6 +138,7 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent event->getDeviceId(), event->getSource(), event->getDisplayId(), event->getAction(), event->getActionButton(), event->getFlags(), event->getEdgeFlags(), event->getMetaState(), event->getButtonState(), + event->getClassification(), event->getXOffset(), event->getYOffset(), event->getXPrecision(), event->getYPrecision(), event->getDownTime(), event->getHistoricalEventTime(i), diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 9ce6df1832a6..897427fd8787 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -642,6 +642,27 @@ static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) { return static_cast<jint>(SurfaceComposerClient::getActiveColorMode(token)); } +static jintArray nativeGetCompositionDataspaces(JNIEnv* env, jclass) { + ui::Dataspace defaultDataspace, wcgDataspace; + ui::PixelFormat defaultPixelFormat, wcgPixelFormat; + if (SurfaceComposerClient::getCompositionPreference(&defaultDataspace, + &defaultPixelFormat, + &wcgDataspace, + &wcgPixelFormat) != NO_ERROR) { + return nullptr; + } + jintArray array = env->NewIntArray(2); + if (array == nullptr) { + jniThrowException(env, "java/lang/OutOfMemoryError", nullptr); + return nullptr; + } + jint* arrayValues = env->GetIntArrayElements(array, 0); + arrayValues[0] = static_cast<jint>(defaultDataspace); + arrayValues[1] = static_cast<jint>(wcgDataspace); + env->ReleaseIntArrayElements(array, arrayValues, 0); + return array; +} + static jboolean nativeSetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj, jint colorMode) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); @@ -660,6 +681,10 @@ static void nativeSetDisplayPowerMode(JNIEnv* env, jclass clazz, jobject tokenOb if (t.duration() > 100ms) ALOGD("Excessive delay in setPowerMode()"); } +static jboolean nativeGetProtectedContentSupport(JNIEnv* env, jclass) { + return static_cast<jboolean>(SurfaceComposerClient::getProtectedContentSupport()); +} + static jboolean nativeClearContentFrameStats(JNIEnv* env, jclass clazz, jlong nativeObject) { SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); status_t err = ctrl->clearLayerFrameStats(); @@ -1016,6 +1041,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetActiveColorMode}, {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveColorMode}, + {"nativeGetCompositionDataspaces", "()[I", + (void*)nativeGetCompositionDataspaces}, {"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;", (void*)nativeGetHdrCapabilities }, {"nativeClearContentFrameStats", "(J)Z", @@ -1028,6 +1055,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetAnimationFrameStats }, {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V", (void*)nativeSetDisplayPowerMode }, + {"nativeGetProtectedContentSupport", "()Z", + (void*)nativeGetProtectedContentSupport }, {"nativeDeferTransactionUntil", "(JJLandroid/os/IBinder;J)V", (void*)nativeDeferTransactionUntil }, {"nativeDeferTransactionUntilSurface", "(JJJJ)V", diff --git a/core/proto/Android.bp b/core/proto/Android.bp new file mode 100644 index 000000000000..80cc2d4cab94 --- /dev/null +++ b/core/proto/Android.bp @@ -0,0 +1,27 @@ +// 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. + +// C++ library for Bluetooth platform wide protobuf definitions +cc_library_static { + name: "libbt-platform-protos-lite", + host_supported: true, + proto: { + export_proto_headers: true, + type: "lite", + }, + srcs: [ + "android/bluetooth/enums.proto", + "android/bluetooth/hci/enums.proto", + ], +} diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 2f2f62392157..45385080d256 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -27,9 +27,569 @@ enum Action { PAGE_VISIBLE = 1; PAGE_HIDE = 2; + // ACTION: Settings > Wi-Fi > [Long press network] > Connect to network + // SUBTYPE: true if connecting to a saved network, false if not + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_WIFI_CONNECT = 135; + + // ACTION: Settings > Wi-Fi > [Long press network] > Forget network + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_WIFI_FORGET = 137; + + // ACTION: Settings > Wi-Fi > Toggle off + // SUBTYPE: true if connected to network before toggle, false if not + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_WIFI_OFF = 138; + + // ACTION: Settings > Wi-Fi > Toggle on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_WIFI_ON = 139; + + // ACTION: Settings > Bluetooth > Overflow > Rename this device + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_BLUETOOTH_RENAME = 161; + + // ACTION: Settings > Bluetooth > Overflow > Show received files + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_BLUETOOTH_FILES = 162; + + // ACTION: DND Settings > Priority only allows > Reminder toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_ALLOW_REMINDERS = 167; + + // ACTION: DND Settings > Priority only allows > Event toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_ALLOW_EVENTS = 168; + + // ACTION: DND Settings > Priority only allows > Messages + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_ALLOW_MESSAGES = 169; + + // ACTION: DND Settings > Priority only allows > Calls + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_ALLOW_CALLS = 170; + + // ACTION: DND Settings > Priority only allows > Repeat callers toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_ALLOW_REPEAT_CALLS = 171; + + // ACTION: DND Settings > Automatic rules > [Rule] > Delete rule > Delete + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_DELETE_RULE_OK = 175; + + // ACTION: Settings > More > Airplane mode toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_AIRPLANE_TOGGLE = 177; + + // ACTION: Settings > Data usage > Cellular data toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_CELL_DATA_TOGGLE = 178; + + // ACTION: Settings > Display > When device is rotated + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ROTATION_LOCK = 203; + + // OPEN: Settings > Search > Perform search + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_SEARCH_RESULTS = 226; + + // ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Delete + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_FINGERPRINT_DELETE = 253; + + // ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Rename + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_FINGERPRINT_RENAME = 254; + + // ACTION: Settings -> Developer Options -> Take bug report -> Interactive report + // CATEGORY: SETTINGS + // OS: N + // Interactive bug report initiated from Settings. + ACTION_BUGREPORT_FROM_SETTINGS_INTERACTIVE = 294; + + // ACTION: Settings -> Developer Options -> Take bug report -> Full report + // CATEGORY: SETTINGS + // OS: N + // Interactive bug report initiated from Settings. + ACTION_BUGREPORT_FROM_SETTINGS_FULL = 295; + + // click on collapsed conditional or clicks expand button + ACTION_SETTINGS_CONDITION_EXPAND = 373; + + // click main area of expanded conditional + ACTION_SETTINGS_CONDITION_CLICK = 375; + + // click a direct button on expanded conditional + ACTION_SETTINGS_CONDITION_BUTTON = 376; + + // Action: user enable / disabled data saver using Settings + // OPEN: Settings -> Data Usage -> Data saver -> On/off toggle + // VALUE: 1 for enabled, 0 for disabled + // CATEGORY: SETTINGS + // OS: N + ACTION_DATA_SAVER_MODE = 394; + + // User whitelisted an app for Data Saver mode; action pass package name of app + // Action: user enable / disabled data saver using Settings + // OPEN: Settings -> Data Usage -> Data saver -> Unrestricted data access > APP toggle turned on + // or + // Settings -> Apps -> APP -> Data usage -> Unrestricted data usage toggle turned on + // VALUE: package name of APP + // CATEGORY: SETTINGS + // OS: N + ACTION_DATA_SAVER_WHITELIST = 395; + + // User blacklisted an app for Data Saver mode; action pass package name of app + // OPEN: Settings -> Apps -> APP -> Data usage -> Background data toggle turned off + // VALUE: package name of APP + // CATEGORY: SETTINGS + // OS: N + ACTION_DATA_SAVER_BLACKLIST = 396; + + // ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager + // SUBTYPE: false is off, true is on + ACTION_TOGGLE_STORAGE_MANAGER = 489; + + // OPEN: Settings > Display -> Ambient Display + // CATEGORY: SETTINGS + ACTION_AMBIENT_DISPLAY = 495; + + // ACTION: Allow Battery optimization for an app + APP_SPECIAL_PERMISSION_BATTERY_ALLOW = 764; + + // ACTION: Deny Battery optimization for an app + APP_SPECIAL_PERMISSION_BATTERY_DENY = 765; + + // ACTION: Enable Device Admin app + APP_SPECIAL_PERMISSION_ADMIN_ALLOW = 766; + + // ACTION: Disable Device Admin app + APP_SPECIAL_PERMISSION_ADMIN_DENY = 767; + + // ACTION: Allow "Do Not Disturb access" for an app + APP_SPECIAL_PERMISSION_DND_ALLOW = 768; + + // ACTION: Deny "Do Not Disturb access" for an app + APP_SPECIAL_PERMISSION_DND_DENY = 769; + + // ACTION: Allow "Draw over other apps" for an app + APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770; + + // ACTION: Deny "Display over other apps" for an app + APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771; + + // ACTION: Allow "VR helper services" for an app + APP_SPECIAL_PERMISSION_VRHELPER_ALLOW = 772; + + // ACTION: Deny "VR helper services" for an app + APP_SPECIAL_PERMISSION_VRHELPER_DENY = 773; + + // ACTION: Allow "Modify system settings" for an app + APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW = 774; + + // ACTION: Deny "Modify system settings" for an app + APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY = 775; + + // ACTION: Allow "Notification access" for an app + APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW = 776; + + // ACTION: Deny "Notification access" for an app + APP_SPECIAL_PERMISSION_NOTIVIEW_DENY = 777; + + // ACTION: "Premium SMS access" for an app - "ask user" option + APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK = 778; + + // ACTION: "Premium SMS access" for an app - "never allow" option + APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY = 779; + + // ACTION: "Premium SMS access" for an app - "always allow" option + APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW = 780; + + // ACTION: Allow "Unrestricted data access" for an app + APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW = 781; + + // ACTION: Deny "Unrestricted data access" for an app + APP_SPECIAL_PERMISSION_UNL_DATA_DENY = 782; + + // ACTION: Allow "Usage access" for an app + APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW = 783; + + // ACTION: Deny "Usage access" for an app + APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY = 784; + + // ACTION: "Force stop" action on an app + ACTION_APP_FORCE_STOP = 807; + + // ACTION: Allow "Enable picture-in-picture" for an app + APP_PICTURE_IN_PICTURE_ALLOW = 813; + + // ACTION: Create a Settings shortcut item. + ACTION_SETTINGS_CREATE_SHORTCUT = 829; + + // ACTION: Settings advanced button is expanded + ACTION_SETTINGS_ADVANCED_BUTTON_EXPAND = 834; + + // ACTION: Deny "Enable picture-in-picture" for an app + APP_PICTURE_IN_PICTURE_DENY = 814; + + // ACTION: Settings -> Display -> Theme + ACTION_THEME = 816; + + // ACTION: Settings > About device > Build number + ACTION_SETTINGS_BUILD_NUMBER_PREF = 847; + + // ACTION: Settings > Battery > Menu > Optimization + ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION = 851; + + // ACTION: Settings > Battery > Menu > Apps Toggle + ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE = 852; + // ACTION: Settings > Any preference is changed ACTION_SETTINGS_PREFERENCE_CHANGE = 853; + // ACTION: Settings > Connected devices > Bluetooth -> Available devices + ACTION_SETTINGS_BLUETOOTH_PAIR = 866; + + // ACTION: Settings > Connected devices > Bluetooth -> Paired devices + ACTION_SETTINGS_BLUETOOTH_CONNECT = 867; + + // ACTION: Settings > Connected devices > Bluetooth -> Connected device + ACTION_SETTINGS_BLUETOOTH_DISCONNECT = 868; + + // ACTION: Settings > Connected devices > Bluetooth -> Error dialog + ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR = 869; + + // ACTION: Settings > Connected devices > Bluetooth master switch Toggle + ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870; + + // ACTION: Settings > App detail > Uninstall + ACTION_SETTINGS_UNINSTALL_APP = 872; + + // ACTION: Settings > App detail > Uninstall Device admin app + ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN = 873; + + // ACTION: Settings > App detail > Disable app + ACTION_SETTINGS_DISABLE_APP = 874; + + // ACTION: Settings > App detail > Enable app + ACTION_SETTINGS_ENABLE_APP = 875; + + // ACTION: Settings > App detail > Clear data + ACTION_SETTINGS_CLEAR_APP_DATA = 876; + + // ACTION: Settings > App detail > Clear cache + ACTION_SETTINGS_CLEAR_APP_CACHE = 877; + + // ACTION: Logs pressing the "Clear app" button in the app info settings page for an instant + // app. + // VALUE: The package name of the app + ACTION_SETTINGS_CLEAR_INSTANT_APP = 923; + + // OPEN: Assist Gesture training intro in Settings + // CATEGORY: SETTINGS + // OS: O DR + SETTINGS_ASSIST_GESTURE_TRAINING_INTRO = 991; + + // OPEN: Assist Gesture training enrolling in Settings + // CATEGORY: SETTINGS + // OS: O DR + SETTINGS_ASSIST_GESTURE_TRAINING_ENROLLING = 992; + + // OPEN: Assist Gesture training finished in Settings + // CATEGORY: SETTINGS + // OS: O DR + SETTINGS_ASSIST_GESTURE_TRAINING_FINISHED = 993; + + // ACTION: Update default app from Settings + ACTION_SETTINGS_UPDATE_DEFAULT_APP = 1000; + + // ACTION: Settings > Wi-Fi > [Long press network] > Sign in to network + // CATEGORY: SETTINGS + // OS: O DR + ACTION_WIFI_SIGNIN = 1008; + + // ACTION: Settings > Notification Settings > Open application notification + // CATEGORY: SETTINGS + // OS: O DR + ACTION_OPEN_APP_NOTIFICATION_SETTING = 1016; + + // ACTION: Settings > App Info > Open app settings + // CATEGORY: SETTINGS + // OS: O DR + ACTION_OPEN_APP_SETTING = 1017; + + // ACTION: Collect PSD Signals + // CATEGORY: SETTINGS + // OS: O DR + ACTION_PSD_LOADER = 1019; + + // OPEN: Settings > Trampoline Intent > Settings page + // CATEGORY: SETTINGS + // OS: O DR + TRAMPOLINE_SETTINGS_EVENT = 1033; + + // ACTION: Logged when user tries to pair a Bluetooth device without name from Settings app + // CATEGORY: SETTINGS + // OS: O MR + ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES = 1096; + + // ACTION: Settings > Network & Internet > Mobile network > Network + // CATEGORY: SETTINGS + ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210; + + // ACTION: DND Settings > Priority only allows > Alarms toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_ALLOW_ALARMS = 1226; + + // ACTION: DND Settings > Priority only allows > Media toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_ALLOW_MEDIA = 1227; + + // ACTION: A private dns mode been selected by user + // CATEGORY: SETTINGS + // OS: P + ACTION_PRIVATE_DNS_MODE = 1249; + + // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name > OK + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK = 1267; + + // OPEN: Settings > Sound > Do Not Disturb > TURN ON NOW/TURN OFF NOW + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_TOGGLE_DND_BUTTON = 1268; + + // ACTION: DND Settings > What to block > full screen intents + // SUBTYPE: false is allowed, true is blocked + // CATEGORY: SETTINGS + // OS: 6.0 + ACTION_ZEN_BLOCK_FULL_SCREEN_INTENTS = 1332; + + // ACTION: DND Settings > What to block + // SUBTYPE: false is allowed, true is blocked + // OS: P + ACTION_ZEN_BLOCK_LIGHT = 1333; + + // ACTION: DND Settings > What to block + // SUBTYPE: false is allowed, true is blocked + // OS: P + ACTION_ZEN_BLOCK_PEEK = 1334; + + // ACTION: DND Settings > What to block + // SUBTYPE: false is allowed, true is blocked + // OS: P + ACTION_ZEN_BLOCK_STATUS = 1335; + + // ACTION: DND Settings > What to block + // SUBTYPE: false is allowed, true is blocked + // OS: P + ACTION_ZEN_BLOCK_BADGE = 1336; + + // ACTION: DND Settings > What to block + // SUBTYPE: false is allowed, true is blocked + // OS: P + ACTION_ZEN_BLOCK_AMBIENT = 1337; + + // ACTION: DND Settings > What to block + // SUBTYPE: false is allowed, true is blocked + // OS: P + ACTION_ZEN_BLOCK_NOTIFICATION_LIST = 1338; + + // ACTION: DND Settings > Priority only allows > System toggle + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_ALLOW_SYSTEM = 1340; + + // ACTION: Settings > Battery settings > Battery tip > App restriction tip + // OS: P + ACTION_APP_RESTRICTION_TIP = 1347; + + // ACTION: Settings > Battery settings > Battery tip > High usage tip + // OS: P + ACTION_HIGH_USAGE_TIP = 1348; + + // ACTION: Settings > Battery settings > Battery tip > Summary tip + // OS: P + ACTION_SUMMARY_TIP = 1349; + + // ACTION: Settings > Battery settings > Battery tip > Smart battery tip + // OS: P + ACTION_SMART_BATTERY_TIP = 1350; + + // ACTION: Settings > Battery settings > Battery tip > Early warning tip + // OS: P + ACTION_EARLY_WARNING_TIP = 1351; + + // ACTION: Settings > Battery settings > Battery tip > Low battery tip + // OS: P + ACTION_LOW_BATTERY_TIP = 1352; + + // ACTION: Settings > Battery settings > Battery tip > App restriction list shown + // OS: P + ACTION_APP_RESTRICTION_TIP_LIST = 1353; + + // ACTION: Settings > Battery settings > Battery tip > High usage list shown + // OS: P + ACTION_HIGH_USAGE_TIP_LIST = 1354; + + // ACTION: Settings > Battery settings > Battery tip > Open app restriction page + // CATEGORY: SETTINGS + // OS: P + ACTION_TIP_OPEN_APP_RESTRICTION_PAGE = 1361; + + // ACTION: Settings > Battery settings > Battery tip > Restrict app + // CATEGORY: SETTINGS + // OS: P + ACTION_TIP_RESTRICT_APP = 1362; + + // ACTION: Settings > Battery settings > Battery tip > Unrestrict app + // CATEGORY: SETTINGS + // OS: P + ACTION_TIP_UNRESTRICT_APP = 1363; + + // ACTION: Settings > Battery settings > Battery tip > Open smart battery page + // CATEGORY: SETTINGS + // OS: P + ACTION_TIP_OPEN_SMART_BATTERY = 1364; + + // ACTION: Settings > Battery settings > Battery tip > Turn on battery saver + // CATEGORY: SETTINGS + // OS: P + ACTION_TIP_TURN_ON_BATTERY_SAVER = 1365; + + // ACTION: Settings > Anomaly receiver > Anomaly received + // CATEGORY: SETTINGS + // OS: P + ACTION_ANOMALY_TRIGGERED = 1367; + + // ACTION: A Settings Slice is requested + // CATEGORY: SETTINGS + // OS: P + ACTION_SETTINGS_SLICE_REQUESTED = 1371; + + // ACTION: A Settings Slice is updated with new value + // CATEGORY: SETTINGS + // OS: P + ACTION_SETTINGS_SLICE_CHANGED = 1372; + + // OPEN: DND onboarding activity > Ok button + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_ONBOARDING_OK = 1378; + + // OPEN: DND onboarding activity > Settings link + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_ONBOARDING_SETTINGS = 1379; + + // ACTION: Settings > Anomaly receiver > Anomaly ignored, don't show up in battery settings + // CATEGORY: SETTINGS + // OS: P + ACTION_ANOMALY_IGNORED = 1387; + + // ACTION: Settings > Battery settings > Battery tip > Open battery saver page + // CATEGORY: SETTINGS + // OS: P + ACTION_TIP_OPEN_BATTERY_SAVER_PAGE = 1388; + + // ACTION: DND Settings > What to block + // OS: P + ACTION_ZEN_SOUND_ONLY = 1396; + + // ACTION: DND Settings > Notifications + // OS: P + ACTION_ZEN_SOUND_AND_VIS_EFFECTS = 1397; + + // ACTION: DND Settings > Notifications + // OS: P + ACTION_ZEN_SHOW_CUSTOM = 1398; + + // ACTION: DND Settings > Notifications + // OS: P + ACTION_ZEN_CUSTOM = 1399; + + // OPEN: DND onboarding activity > don't update button + // CATEGORY: SETTINGS + // OS: P + ACTION_ZEN_ONBOARDING_KEEP_CURRENT_SETTINGS = 1406; + + // ACTION: Storage initialization wizard initialization choice of external/portable + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_INIT_EXTERNAL = 1407; + + // ACTION: Storage initialization wizard initialization choice of internal/adoptable + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_INIT_INTERNAL = 1408; + + // ACTION: Storage initialization wizard benchmark fast choice of continue + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_BENCHMARK_FAST_CONTINUE = 1409; + + // ACTION: Storage initialization wizard benchmark slow choice of continue + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_BENCHMARK_SLOW_CONTINUE = 1410; + + // ACTION: Storage initialization wizard benchmark slow choice of abort + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_BENCHMARK_SLOW_ABORT = 1411; + + // ACTION: Storage initialization wizard migration choice of now + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_MIGRATE_NOW = 1412; + + // ACTION: Storage initialization wizard migration choice of later + // CATEGORY: SETTINGS + // OS: P + ACTION_STORAGE_MIGRATE_LATER = 1413; + + // OPEN: Settings > Sound > Switch a2dp devices dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_SWITCH_A2DP_DEVICES = 1415; + + + // OPEN: Settings > Sound > Switch hfp devices dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_SWITCH_HFP_DEVICES = 1416; + // ACTION: Tap & Pay -> Default Application Setting -> Use Forground ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622; @@ -44,15 +604,1454 @@ enum PageId { // Unknown page. Should not be used in production code. PAGE_UNKNOWN = 0; + // OPEN: Settings > Accessibility + // CATEGORY: SETTINGS + // OS: 6.0 + ACCESSIBILITY = 2; + + // OPEN: Settings > Accessibility > Captions + // CATEGORY: SETTINGS + // OS: 6.0 + ACCESSIBILITY_CAPTION_PROPERTIES = 3; + + // OPEN: Settings > Accessibility > [Service] + // CATEGORY: SETTINGS + // OS: 6.0 + ACCESSIBILITY_SERVICE = 4; + + // OPEN: Settings > Accessibility > Color correction + // CATEGORY: SETTINGS + // OS: 6.0 + ACCESSIBILITY_TOGGLE_DALTONIZER = 5; + + // OPEN: Settings > Accessibility > Accessibility shortcut + // CATEGORY: SETTINGS + // OS: 6.0 + ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6; + + // OPEN: Settings > Accessibility > Magnification gestures (Renamed in O) + // OPEN: Settings > Accessibility > Magnification > Magnify with triple-tap + // OPEN: Settings > Accessibility > Magnification > Magnify with button + // CATEGORY: SETTINGS + // OS: 6.0 + ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7; + + // OPEN: Settings > Accounts + // CATEGORY: SETTINGS + // OS: 6.0 + ACCOUNT = 8; + + // OPEN: Settings > Accounts > [Single Account Sync Settings] + // CATEGORY: SETTINGS + // OS: 6.0 + ACCOUNTS_ACCOUNT_SYNC = 9; + + // OPEN: Settings > Accounts > Add an account + // CATEGORY: SETTINGS + // OS: 6.0 + ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10; + + // OPEN: Settings > Cellular network settings > APNs + // CATEGORY: SETTINGS + // OS: 6.0 + APN = 12; + + // OPEN: Settings > More > Cellular network settings > APNs > [Edit APN] + // CATEGORY: SETTINGS + // OS: 6.0 + APN_EDITOR = 13; + + // OPEN: Settings > Apps > Configure apps > App links > [App] + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_APP_LAUNCH = 17; + + // OPEN: Settings > Internal storage > Apps storage > [App] + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_APP_STORAGE = 19; + + // OPEN: Settings > Apps > [App info] + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_INSTALLED_APP_DETAILS = 20; + + // OPEN: Settings > Memory > App usage > [App Memory usage] + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_PROCESS_STATS_DETAIL = 21; + + // OPEN: Settings > Memory > App usage + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_PROCESS_STATS_UI = 23; + + // OPEN: Choose Bluetooth device (ex: when sharing) + // CATEGORY: SETTINGS + // OS: 6.0 + BLUETOOTH_DEVICE_PICKER = 25; + + // OPEN: Settings > Security > Choose screen lock + // CATEGORY: SETTINGS + // OS: 6.0 + CHOOSE_LOCK_GENERIC = 27; + + // OPEN: Settings > Security > Choose screen lock > Choose your password + // CATEGORY: SETTINGS + // OS: 6.0 + CHOOSE_LOCK_PASSWORD = 28; + + // OPEN: Settings > Security > Choose screen lock > Choose your pattern + // CATEGORY: SETTINGS + // OS: 6.0 + CHOOSE_LOCK_PATTERN = 29; + + // OPEN: Settings > Security > Choose screen lock > Confirm your password + // CATEGORY: SETTINGS + // OS: 6.0 + CONFIRM_LOCK_PASSWORD = 30; + + // OPEN: Settings > Security > Choose screen lock > Confirm your pattern + // CATEGORY: SETTINGS + // OS: 6.0 + CONFIRM_LOCK_PATTERN = 31; + + // OPEN: Settings > Security > Encrypt phone + // CATEGORY: SETTINGS + // OS: 6.0 + CRYPT_KEEPER = 32; + + // OPEN: Settings > Security > Encrypt phone > Confirm + // CATEGORY: SETTINGS + // OS: 6.0 + CRYPT_KEEPER_CONFIRM = 33; + + // OPEN: Settings (Root page) + // CATEGORY: SETTINGS + // OS: 6.0 + DASHBOARD_SUMMARY = 35; + + // OPEN: Settings > Data usage + // CATEGORY: SETTINGS + // OS: 6.0 + DATA_USAGE_SUMMARY = 37; + + // OPEN: Settings > Date & time + // CATEGORY: SETTINGS + // OS: 6.0 + DATE_TIME = 38; + + // OPEN: Settings > Developer options + // CATEGORY: SETTINGS + // OS: 6.0 + DEVELOPMENT = 39; + + // OPEN: Settings > About phone + // CATEGORY: SETTINGS + // OS: 6.0 + DEVICEINFO = 40; + + // OPEN: Settings > Internal storage + // CATEGORY: SETTINGS + // OS: 6.0 + DEVICEINFO_STORAGE = 42; + + // OPEN: Settings > Display + // CATEGORY: SETTINGS + // OS: 6.0 + DISPLAY = 46; + + // OPEN: Settings > Display > Daydream + // CATEGORY: SETTINGS + // OS: 6.0 + DREAM = 47; + + // OPEN: Settings > Security > Screen lock > Secure start-up + // CATEGORY: SETTINGS + // OS: 6.0 + ENCRYPTION = 48; + + // OPEN: Settings > Security > Nexus Imprint + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT = 49; + + // OPEN: Settings > Battery > History details + // CATEGORY: SETTINGS + // OS: 6.0 + FUELGAUGE_BATTERY_HISTORY_DETAIL = 51; + + // OPEN: Settings > Battery > Battery saver + // CATEGORY: SETTINGS + // OS: 6.0 + FUELGAUGE_BATTERY_SAVER = 52; + + // OPEN: Settings > Battery > [App Use details] + // CATEGORY: SETTINGS + // OS: 6.0 + FUELGAUGE_POWER_USAGE_DETAIL = 53; + + // OPEN: Settings > Security > SIM card lock settings + // CATEGORY: SETTINGS + // OS: 6.0 + ICC_LOCK = 56; + + // OPEN: Settings > Language & input > Physical keyboard + // CATEGORY: SETTINGS + // OS: 6.0 + INPUTMETHOD_KEYBOARD = 58; + + // OPEN: Settings > Language & input > Spell checker + // CATEGORY: SETTINGS + // OS: 6.0 + INPUTMETHOD_SPELL_CHECKERS = 59; + + // OBSOLETE + INPUTMETHOD_SUBTYPE_ENABLER = 60; + + // OPEN: Settings > Language & input > Personal dictionary + // CATEGORY: SETTINGS + // OS: 6.0 + INPUTMETHOD_USER_DICTIONARY = 61; + + // OPEN: Settings > Language & input > Add word + // CATEGORY: SETTINGS + // OS: 6.0 + INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62; + + // OPEN: Settings > Location + // CATEGORY: SETTINGS + // OS: 6.0 + LOCATION = 63; + + // OPEN: Settings > Apps + // CATEGORY: SETTINGS + // OS: 6.0 + MANAGE_APPLICATIONS = 65; + + // OPEN: Settings > Backup & reset > Factory data reset + // CATEGORY: SETTINGS + // OS: 6.0 + MASTER_CLEAR = 66; + + // OPEN: Settings > Backup & reset > Factory data reset > Confirm + // CATEGORY: SETTINGS + // OS: 6.0 + MASTER_CLEAR_CONFIRM = 67; + + // OPEN: Settings > More > Android Beam + // CATEGORY: SETTINGS + // OS: 6.0 + NFC_BEAM = 69; + + // OPEN: Settings > Tap & pay + // CATEGORY: SETTINGS + // OS: 6.0 + NFC_PAYMENT = 70; + + // OPEN: Settings > Sound & notification > App notifications > [App] + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_APP_NOTIFICATION = 72; + + // OBSOLETE + NOTIFICATION_REDACTION = 74; + + // OPEN: Settings Widget > Notification log + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_STATION = 75; + + // OPEN: Settings > Sound & notification > Do not disturb + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ZEN_MODE = 76; + + + // OPEN: Print job notification > Print job settings + // CATEGORY: SETTINGS + // OS: 6.0 + PRINT_JOB_SETTINGS = 78; + + // OPEN: Settings > Printing > [Print Service] + // CATEGORY: SETTINGS + // OS: 6.0 + PRINT_SERVICE_SETTINGS = 79; + + // OPEN: Settings > Printing + // CATEGORY: SETTINGS + // OS: 6.0 + PRINT_SETTINGS = 80; + + // OPEN: Settings > Backup & reset + // CATEGORY: SETTINGS + // OS: 6.0 + PRIVACY = 81; + + //OBSOLETE + PROXY_SELECTOR = 82; + + // OPEN: Settings > Backup & reset > Network settings reset + // CATEGORY: SETTINGS + // OS: 6.0 + RESET_NETWORK = 83; + + // OPEN: Settings > Backup & reset > Network settings reset > Confirm + // CATEGORY: SETTINGS + // OS: 6.0 + RESET_NETWORK_CONFIRM = 84; + + // OPEN: Settings > Developer Options > Running Services + // CATEGORY: SETTINGS + // OS: 6.0 + RUNNING_SERVICE_DETAILS = 85; + + // OPEN: Settings > Security > Screen pinning + // CATEGORY: SETTINGS + // OS: 6.0 + SCREEN_PINNING = 86; + + // OPEN: Settings > Security + // CATEGORY: SETTINGS + // OS: 6.0 + SECURITY = 87; + + // OPEN: Settings > SIM cards + // CATEGORY: SETTINGS + // OS: 6.0 + SIM = 88; + + // OBSOLETE + TESTING = 89; + + // OPEN: Settings > More > Tethering & portable hotspot + // CATEGORY: SETTINGS + // OS: 6.0 + TETHER = 90; + + // OPEN: Settings > Security > Trust agents + // CATEGORY: SETTINGS + // OS: 6.0 + TRUST_AGENT = 91; + + // OPEN: Settings > Security > Trusted credentials + // CATEGORY: SETTINGS + // OS: 6.0 + TRUSTED_CREDENTIALS = 92; + + // OPEN: Settings > Language & input > TTS output > [Engine] > Settings + // CATEGORY: SETTINGS + // OS: 6.0 + TTS_ENGINE_SETTINGS = 93; + + // OPEN: Settings > Language & input > Text-to-speech output + // CATEGORY: SETTINGS + // OS: 6.0 + TTS_TEXT_TO_SPEECH = 94; + + // OPEN: Settings > Security > Apps with usage access + // CATEGORY: SETTINGS + // OS: 6.0 + USAGE_ACCESS = 95; + + // OPEN: Settings > Users + // CATEGORY: SETTINGS + // OS: 6.0 + USER = 96; + + // OPEN: Settings > Users > [Restricted profile app & content access] + // CATEGORY: SETTINGS + // OS: 6.0 + USERS_APP_RESTRICTIONS = 97; + + // OPEN: Settings > Users > [User settings] + // CATEGORY: SETTINGS + // OS: 6.0 + USER_DETAILS = 98; + + // OPEN: Settings > More > VPN + // CATEGORY: SETTINGS + // OS: 6.0 + VPN = 100; + + // OPEN: Settings > Display > Choose wallpaper from + // CATEGORY: SETTINGS + // OS: 6.0 + WALLPAPER_TYPE = 101; + + // OPEN: Settings > Display > Cast + // CATEGORY: SETTINGS + // OS: 6.0 + WFD_WIFI_DISPLAY = 102; + + // OPEN: Settings > Wi-Fi + // CATEGORY: SETTINGS + // OS: 6.0 + WIFI = 103; + + // OPEN: Settings > More > Wi-Fi Calling + // CATEGORY: SETTINGS + // OS: 6.0 + WIFI_CALLING = 105; + + // OPEN: Settings > Wi-Fi > Saved networks + // CATEGORY: SETTINGS + // OS: 6.0 + WIFI_SAVED_ACCESS_POINTS = 106; + + // OPEN: Settings > Wi-Fi > Advanced Wi-Fi > Wi-Fi Direct + // CATEGORY: SETTINGS + // OS: 6.0 + WIFI_P2P = 109; + + // OPEN: Settings > Apps > Configure apps > App permissions + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_ADVANCED = 130; + + // OPEN: Settings > Location > Scanning + // CATEGORY: SETTINGS + // OS: 6.0 + LOCATION_SCANNING = 131; + + // OPEN: Settings > Sound & notification > App notifications + // CATEGORY: SETTINGS + // OS: 6.0 + MANAGE_APPLICATIONS_NOTIFICATIONS = 133; + + // OPEN: Settings > Sound & notification > DND > Priority only allows + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ZEN_MODE_PRIORITY = 141; + + // OPEN: Settings > Sound & notification > DND > Automatic rules + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ZEN_MODE_AUTOMATION = 142; + + // OPEN: Settings > Sound & notification > DND > [Time based rule] + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; + + // OPEN: Settings > Apps > Configure apps > App links + // CATEGORY: SETTINGS + // OS: 6.0 + MANAGE_DOMAIN_URLS = 143; + + // OPEN: Settings > Sound & notification > DND > [Event rule] + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ZEN_MODE_EVENT_RULE = 146; + + // OPEN: Settings > Sound & notification > Notification access + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ACCESS = 179; + + // OPEN: Settings > Sound & notification > Do Not Disturb access + // CATEGORY: SETTINGS + // OS: 6.0 + NOTIFICATION_ZEN_MODE_ACCESS = 180; + + // OPEN: Settings > Internal storage > Apps storage + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_STORAGE_APPS = 182; + + // OPEN: Settings > Security > Usage access + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_USAGE_ACCESS_DETAIL = 183; + + // OPEN: Settings > Battery > Battery optimization + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_HIGH_POWER_APPS = 184; + + // OPEN: Settings > Apps > Configure > Default apps > Assist & voice input + // CATEGORY: SETTINGS + // OS: 6.0 + APPLICATIONS_MANAGE_ASSIST = 201; + + // OPEN: Settings > Memory + // CATEGORY: SETTINGS + // OS: 6.0 + PROCESS_STATS_SUMMARY = 202; + + // OPEN: Settings > Apps > Configure Apps > Display over other apps + // CATEGORY: SETTINGS + // OS: 6.0 + SYSTEM_ALERT_WINDOW_APPS = 221; + + // OPEN: Settings > About phone > Legal information + // CATEGORY: SETTINGS + // OS: 6.0 + ABOUT_LEGAL_SETTINGS = 225; + + + // OPEN: Settings > Developer options > Inactive apps + // CATEGORY: SETTINGS + // OS: 6.0 + FUELGAUGE_INACTIVE_APPS = 238; + + // OPEN: Settings > Security > Nexus Imprint > Add Fingerprint + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLLING = 240; + // OPEN: Fingerprint Enroll > Find Sensor + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_FIND_SENSOR = 241; + + // OPEN: Fingerprint Enroll > Fingerprint Enrolled! + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLL_FINISH = 242; + + // OPEN: Fingerprint Enroll introduction + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLL_INTRO = 243; + + // OPEN: Fingerprint Enroll > Let's Start! + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLL_SIDECAR = 245; + + // OPEN: Fingerprint Enroll SUW > Let's Start! + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLLING_SETUP = 246; + + // OPEN: Fingerprint Enroll SUW > Find Sensor + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_FIND_SENSOR_SETUP = 247; + + // OPEN: Fingerprint Enroll SUW > Fingerprint Enrolled! + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLL_FINISH_SETUP = 248; + + // OPEN: Fingerprint Enroll SUW introduction + // CATEGORY: SETTINGS + // OS: 6.0 + FINGERPRINT_ENROLL_INTRO_SETUP = 249; + + // OPEN: Settings > Developer Options > Background Check + // CATEGORY: SETTINGS + // OS: N + BACKGROUND_CHECK_SUMMARY = 258; + + // OPEN: Settings > Notifications > [App] > Channel Notifications + // CATEGORY: SETTINGS + // OS: N + NOTIFICATION_TOPIC_NOTIFICATION = 265; + + // OPEN: Settings > Security > User credentials + // CATEGORY: Settings + // OS: N + USER_CREDENTIALS = 285; + + // Logs that the user has edited the enabled VR listeners. + // CATEGORY: SETTINGS + // OS: N + VR_MANAGE_LISTENERS = 334; + + // Settings -> Accessibility -> Click after pointer stops moving + // CATEGORY: SETTINGS + // OS: N + ACCESSIBILITY_TOGGLE_AUTOCLICK = 335; + + // Settings -> Sound + // CATEGORY: SETTINGS + // OS: N + SOUND = 336; + + // Settings -> Notifications -> Gear + // CATEGORY: SETTINGS + // OS: N + CONFIGURE_NOTIFICATION = 337; + + // Settings -> Wi-Fi -> Gear + // CATEGORY: SETTINGS + // OS: N + CONFIGURE_WIFI = 338; + + // Settings -> Display -> Display size + // OS: N + DISPLAY_SCREEN_ZOOM = 339; + + // Settings -> Display -> Font size + // CATEGORY: SETTINGS + // OS: N + ACCESSIBILITY_FONT_SIZE = 340; + + // Settings -> Data usage -> Cellular/Wi-Fi data usage + // CATEGORY: SETTINGS + // OS: N + DATA_USAGE_LIST = 341; + + // Settings -> Data usage -> Billing cycle or DATA_USAGE_LIST -> Gear + // CATEGORY: SETTINGS + // OS: N + BILLING_CYCLE = 342; + + // DATA_USAGE_LIST -> Any item or App info -> Data usage + // CATEGORY: SETTINGS + // OS: N + APP_DATA_USAGE = 343; + + // Settings -> Language & input -> Language + // CATEGORY: SETTINGS + // OS: N + USER_LOCALE_LIST = 344; + + // Settings -> Language & input -> Virtual keyboard + // CATEGORY: SETTINGS + // OS: N + VIRTUAL_KEYBOARDS = 345; + + // Settings -> Language & input -> Physical keyboard + // CATEGORY: SETTINGS + // OS: N + PHYSICAL_KEYBOARDS = 346; + + // Settings -> Language & input -> Virtual keyboard -> Add a virtual keyboard + // CATEGORY: SETTINGS + // OS: N + ENABLE_VIRTUAL_KEYBOARDS = 347; + + // Settings -> Data usage -> Data Saver + // CATEGORY: SETTINGS + // OS: N + DATA_SAVER_SUMMARY = 348; + + // Settings -> Data usage -> Data Saver -> Unrestricted data access + // CATEGORY: SETTINGS + // OS: N + DATA_USAGE_UNRESTRICTED_ACCESS = 349; + + // Settings -> Apps -> Gear -> Special access + SPECIAL_ACCESS = 351; + + // OPEN: SUW Welcome Screen -> Vision Settings + // CATEGORY: SETTINGS + // OS: N + SUW_ACCESSIBILITY = 367; + + // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification gestures (Renamed in O) + // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification -> Magnify with triple-tap + // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification -> Magnify with button + // ACTION: New magnification gesture configuration is chosen + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: N + SUW_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 368; + + // OPEN: SUW Welcome Screen -> Vision Settings -> Font size + // ACTION: New font size is chosen + // SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is largest + // CATEGORY: SETTINGS + // OS: N + SUW_ACCESSIBILITY_FONT_SIZE = 369; + + // OPEN: SUW Welcome Screen -> Vision Settings -> Display size + // ACTION: New display size is chosen + // SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is larger, 4 is largest + // CATEGORY: SETTINGS + // OS: N + SUW_ACCESSIBILITY_DISPLAY_SIZE = 370; + + // OPEN: SUW Welcome Screen -> Vision Settings -> TalkBack + // ACTION: New screen reader configuration is chosen + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: N + SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER = 371; + + // Airplane mode on + SETTINGS_CONDITION_AIRPLANE_MODE = 377; + // AKA Data saver on + SETTINGS_CONDITION_BACKGROUND_DATA = 378; + // Battery saver on + SETTINGS_CONDITION_BATTERY_SAVER = 379; + // Cellular data off + SETTINGS_CONDITION_CELLULAR_DATA = 380; + // Do not disturb on + SETTINGS_CONDITION_DND = 381; + // Hotspot on + SETTINGS_CONDITION_HOTSPOT = 382; + // Work profile off + SETTINGS_CONDITION_WORK_MODE = 383; + + // Settings > Apps > Gear > Special Access > Premium SMS access + PREMIUM_SMS_ACCESS = 388; + + // OPEN: Settings > Accounts > Work profile settings + // CATEGORY: SETTINGS + ACCOUNTS_WORK_PROFILE_SETTINGS = 401; + + // Settings -> Dev options -> Convert to file encryption + CONVERT_FBE = 402; + + // Settings -> Dev options -> Convert to file encryption -> WIPE AND CONVERT... + CONVERT_FBE_CONFIRM = 403; + + // Settings -> Dev options -> Running services + RUNNING_SERVICES = 404; + + // The dialog shown by 3P intent to change current webview implementation. + WEBVIEW_IMPLEMENTATION = 405; + + // OPEN: Settings > Internal storage > Storage manager + // CATEGORY: SETTINGS + STORAGE_MANAGER_SETTINGS = 458; + + // OPEN: Settings -> Gestures + // CATEGORY: SETTINGS + SETTINGS_GESTURES = 459; + + // OPEN: Settings > Display > Night Light + // CATEGORY: SETTINGS + NIGHT_DISPLAY_SETTINGS = 488; + + // Night Light on + SETTINGS_CONDITION_NIGHT_DISPLAY = 492; + + // OPEN: Settings > Language & input > Personal dictionary (single locale) + USER_DICTIONARY_SETTINGS = 514; + + // OPEN: Settings > Date & time > Select time zone + ZONE_PICKER = 515; + + // OPEN: Settings > Security > Device administrators + DEVICE_ADMIN_SETTINGS = 516; + + // OPEN: Settings > Security > Factory Reset Protection dialog + DIALOG_FRP = 528; + + // OPEN: Settings > Custom list preference with confirmation message + DIALOG_CUSTOM_LIST_CONFIRMATION = 529; + + // OPEN: Settings > APN Editor > Error dialog + DIALOG_APN_EDITOR_ERROR = 530; + + // OPEN: Settings > Users > Edit owner info dialog + DIALOG_OWNER_INFO_SETTINGS = 531; + + // OPEN: Settings > Security > Use one lock dialog + DIALOG_UNIFICATION_CONFIRMATION = 532; + + // OPEN: Settings > Security > User Credential + DIALOG_USER_CREDENTIAL = 533; + + // OPEN: Settings > Accounts > Remove account + DIALOG_REMOVE_USER = 534; + + // OPEN: Settings > Accounts > Confirm auto sync dialog + DIALOG_CONFIRM_AUTO_SYNC_CHANGE = 535; + + // OPEN: Settings > Apps > Dialog for running service details + DIALOG_RUNNIGN_SERVICE = 536; + + // OPEN: Settings > Bluetooth > Rename this device + DIALOG_BLUETOOTH_RENAME = 538; + + // OPEN: Settings > Battery optimization > details for app + DIALOG_HIGH_POWER_DETAILS = 540; + + // OPEN: Settings > Keyboard > Show keyboard layout dialog + DIALOG_KEYBOARD_LAYOUT = 541; + + // OPEN: Settings > WIFI Scan permission dialog + DIALOG_WIFI_SCAN_MODE = 543; + + // OPEN: Settings > Wireless > VPN > Config dialog + DIALOG_LEGACY_VPN_CONFIG = 545; + + // OPEN: Settings > Wireless > VPN > Config dialog for app + DIALOG_VPN_APP_CONFIG = 546; + + // OPEN: Settings > Wireless > VPN > Cannot connect dialog + DIALOG_VPN_CANNOT_CONNECT = 547; + + // OPEN: Settings > Wireless > VPN > Replace existing VPN dialog + DIALOG_VPN_REPLACE_EXISTING = 548; + + // OPEN: Settings > Billing cycle > Edit billing cycle dates dialog + DIALOG_BILLING_CYCLE = 549; + + // OPEN: Settings > Billing cycle > Edit data limit/warning dialog + DIALOG_BILLING_BYTE_LIMIT = 550; + + // OPEN: Settings > Billing cycle > turn on data limit dialog + DIALOG_BILLING_CONFIRM_LIMIT = 551; + + // OPEN: Settings > Service > Turn off notification access dialog + DIALOG_DISABLE_NOTIFICATION_ACCESS = 552; + + // OPEN: Settings > Sound > Use personal sound for work profile dialog + DIALOG_UNIFY_SOUND_SETTINGS = 553; + + // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being granted. + DIALOG_ZEN_ACCESS_GRANT = 554; + + // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being revoked. + DIALOG_ZEN_ACCESS_REVOKE = 555; + + // OPEN: Settings > Zen mode > Dialog that picks time for zen mode. + DIALOG_ZEN_TIMEPICKER = 556; + + // OPEN: Settings > Apps > Dialog that informs user to allow service access for app. + DIALOG_SERVICE_ACCESS_WARNING = 557; + + // OPEN: Settings > Apps > Dialog for app actions (such as force stop/clear data) + DIALOG_APP_INFO_ACTION = 558; + + // OPEN: Settings > Storage > Dialog for forgetting a storage device + DIALOG_VOLUME_FORGET = 559; + + // OPEN: Settings > Storage > Dialog for initializing a volume + DIALOG_VOLUME_INIT = 561; + + // OPEN: Settings > Storage > Dialog for unmounting a volume + DIALOG_VOLUME_UNMOUNT = 562; + + // OPEN: Settings > Storage > Dialog for renaming a volume + DIALOG_VOLUME_RENAME = 563; + + // OPEN: Settings > Storage > Dialog for clear cache + DIALOG_STORAGE_CLEAR_CACHE = 564; + + // OPEN: Settings > Storage > Dialog for system info + DIALOG_STORAGE_SYSTEM_INFO = 565; + + // OPEN: Settings > Storage > Dialog for other info + DIALOG_STORAGE_OTHER_INFO = 566; + + // OPEN: Settings > Storage > Dialog for user info + DIALOG_STORAGE_USER_INFO = 567; + // OPEN: Settings > Add fingerprint > Dialog when user touches fingerprint icon. + DIALOG_FINGERPRINT_ICON_TOUCH = 568; + + // OPEN: Settings > Add fingerprint > Error dialog + DIALOG_FINGERPINT_ERROR = 569; + + // OPEN: Settings > Fingerprint > Rename or delete dialog + DIALOG_FINGERPINT_EDIT = 570; + + // OPEN: Settings > Fingerprint > Dialog for deleting last fingerprint + DIALOG_FINGERPINT_DELETE_LAST = 571; + + // OPEN: SUW > Fingerprint > Dialog to confirm skip fingerprint setup entirely. + DIALOG_FINGERPRINT_SKIP_SETUP = 573; + + // OPEN: Settings > Proxy Selector error dialog + DIALOG_PROXY_SELECTOR_ERROR = 574; + + // OPEN: Settings > Wifi > P2P Settings > Disconnect dialog + DIALOG_WIFI_P2P_DISCONNECT = 575; + + // OPEN: Settings > Wifi > P2P Settings > Cancel connection dialog + DIALOG_WIFI_P2P_CANCEL_CONNECT = 576; + + // OPEN: Settings > Wifi > P2P Settings > Rename dialog + DIALOG_WIFI_P2P_RENAME = 577; + + // OPEN: Settings > Wifi > P2P Settings > Forget group dialog + DIALOG_WIFI_P2P_DELETE_GROUP = 578; + + // OPEN: Settings > APN > Restore default dialog + DIALOG_APN_RESTORE_DEFAULT = 579; + + // OPEN: Settings > Encryption interstitial accessibility warning dialog + DIALOG_ENCRYPTION_INTERSTITIAL_ACCESSIBILITY = 581; + + // OPEN: Settings > Acessibility > Enable accessiblity service dialog + DIALOG_ACCESSIBILITY_SERVICE_ENABLE = 583; + + // OPEN: Settings > Acessibility > Disable accessiblity service dialog + DIALOG_ACCESSIBILITY_SERVICE_DISABLE = 584; + + // OPEN: Settings > Account > Remove account dialog + DIALOG_ACCOUNT_SYNC_REMOVE = 585; + + // OPEN: Settings > Account > Remove account failed dialog + DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL = 586; + + // OPEN: Settings > Account > Cannot do onetime sync dialog + DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC = 587; + + // OPEN: Settings > Display > Night light > Set start time dialog + DIALOG_NIGHT_DISPLAY_SET_START_TIME = 588; + + // OPEN: Settings > Display > Night light > Set end time dialog + DIALOG_NIGHT_DISPLAY_SET_END_TIME = 589; + + + + // OPEN: Settings > User > Edit info dialog + DIALOG_USER_EDIT = 590; + + // OPEN: Settings > User > Confirm remove dialog + DIALOG_USER_REMOVE = 591; + + // OPEN: Settings > User > Enable calling dialog + DIALOG_USER_ENABLE_CALLING = 592; + + // OPEN: Settings > User > Enable calling and sms dialog + DIALOG_USER_ENABLE_CALLING_AND_SMS = 593; + + // OPEN: Settings > User > Cannot manage device message dialog + DIALOG_USER_CANNOT_MANAGE = 594; + + // OPEN: Settings > User > Add user dialog + DIALOG_USER_ADD = 595; + + // OPEN: Settings > User > Setup user dialog + DIALOG_USER_SETUP = 596; + + // OPEN: Settings > User > Setup profile dialog + DIALOG_USER_SETUP_PROFILE = 597; + + // OPEN: Settings > User > Choose user type dialog + DIALOG_USER_CHOOSE_TYPE = 598; + + // OPEN: Settings > User > Need lockscreen dialog + DIALOG_USER_NEED_LOCKSCREEN = 599; + + // OPEN: Settings > User > Confirm exit guest mode dialog + DIALOG_USER_CONFIRM_EXIT_GUEST = 600; + + // OPEN: Settings > User > Edit user profile dialog + DIALOG_USER_EDIT_PROFILE = 601; + + + // OPEN: Settings > Wifi > Saved AP > Edit dialog + DIALOG_WIFI_SAVED_AP_EDIT = 602; + + // OPEN: Settings > Wifi > Edit AP dialog + DIALOG_WIFI_AP_EDIT = 603; + + // OPEN: Settings > Wifi > Write config to NFC dialog + DIALOG_WIFI_WRITE_NFC = 606; + + // OPEN: Settings > Date > Date picker dialog + DIALOG_DATE_PICKER = 607; + + // OPEN: Settings > Date > Time picker dialog + DIALOG_TIME_PICKER = 608; + + // OPEN: Settings > Wireless > Manage wireless plan dialog + DIALOG_MANAGE_MOBILE_PLAN = 609; + + // OPEN Settings > Bluetooth > Attempt to connect to device that shows dialog + BLUETOOTH_DIALOG_FRAGMENT = 613; + + // OPEN: Settings > Security + // CATEGORY: SETTINGS + // OS: O + ENTERPRISE_PRIVACY_SETTINGS = 628; + + // OPEN: Settings > System + SETTINGS_SYSTEM_CATEGORY = 744; + + // OPEN: Settings > Storage + SETTINGS_STORAGE_CATEGORY = 745; + + // OPEN: Settings > Network & Internet + SETTINGS_NETWORK_CATEGORY = 746; + + // OPEN: Settings > Connected Device + SETTINGS_CONNECTED_DEVICE_CATEGORY = 747; + + // OPEN: Settings > App & Notification + SETTINGS_APP_NOTIF_CATEGORY = 748; + + // OPEN: Settings > System > Language & Region + SETTINGS_LANGUAGE_CATEGORY = 750; + + // OPEN: Settings > System > Input & Gesture > Swipe to notification gesture + SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751; + + // OPEN: Settings > System > Input & Gesture > Double tap power button gesture + SETTINGS_GESTURE_DOUBLE_TAP_POWER = 752; + + // OPEN: Settings > System > Input & Gesture > Pick up gesture + SETTINGS_GESTURE_PICKUP = 753; + + // OPEN: Settings > System > Input & Gesture > Double tap screen gesture + SETTINGS_GESTURE_DOUBLE_TAP_SCREEN = 754; + + // OPEN: Settings > System > Input & Gesture > Double twist gesture + SETTINGS_GESTURE_DOUBLE_TWIST = 755; + + // OPEN: Settings > Apps > Default Apps > Default browser + DEFAULT_BROWSER_PICKER = 785; + // OPEN: Settings > Apps > Default Apps > Default emergency app + DEFAULT_EMERGENCY_APP_PICKER = 786; + + // OPEN: Settings > Apps > Default Apps > Default home + DEFAULT_HOME_PICKER = 787; + + // OPEN: Settings > Apps > Default Apps > Default phone + DEFAULT_PHONE_PICKER = 788; + + // OPEN: Settings > Apps > Default Apps > Default sms + DEFAULT_SMS_PICKER = 789; + + // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection + DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791; + + // OPEN: Settings > Apps > Default Apps > Default autofill app + DEFAULT_AUTOFILL_PICKER = 792; + + // OPEN: Settings > Apps > Gear > Special Access > Install other apps + // CATEGORY: SETTINGS + // OS: 8.0 + MANAGE_EXTERNAL_SOURCES = 808; + + // Logs that the user has edited the picture-in-picture settings. + // CATEGORY: SETTINGS + SETTINGS_MANAGE_PICTURE_IN_PICTURE = 812; + + // OPEN: SUW Welcome Screen -> Vision Settings -> Select to Speak + // ACTION: Select to Speak configuration is chosen + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: SETTINGS + // OS: N + SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK = 817; + + // OPEN: Settings > System > Backup + // CATEGORY: SETTINGS + // OS: O + BACKUP_SETTINGS = 818; + + // OPEN: Settings > Storage > Games + // CATEGORY: SETTINGS + // OS: O + APPLICATIONS_STORAGE_GAMES = 838; + + // OPEN: Settings > Storage > Audio and Music + // CATEGORY: SETTINGS + // OS: O + APPLICATIONS_STORAGE_MUSIC = 839; + + // ACTION: Settings > Storage > Free Up Space to launch Deletion Helper + // CATEGORY: SETTINGS + // OS: O + STORAGE_FREE_UP_SPACE_NOW = 840; + + // ACTION: Settings > Storage > Files to open the File Manager + // CATEGORY: SETTINGS + // OS: O + STORAGE_FILES = 841; + + // OPEN: Settings > Apps > Default Apps > Assist > Default assist + DEFAULT_ASSIST_PICKER = 843; + + // OPEN: Settings > Apps > Default Apps > Assist > Default voice input + DEFAULT_VOICE_INPUT_PICKER = 844; + + // OPEN: Settings > Storage > [Profile] + SETTINGS_STORAGE_PROFILE = 845; + + // OPEN: Settings > Security & screen lock -> Encryption & crendentials + // CATEGORY: SETTINGS + // OS: O + ENCRYPTION_AND_CREDENTIAL = 846; + + // OPEN: Settings > Wi-Fi > Network Details (click on Access Point) + // CATEGORY: SETTINGS + // OS: O + WIFI_NETWORK_DETAILS = 849; + + // OPEN: Settings > Wi-Fi > Wifi Preferences -> Advanced -> Network Scorer + // CATEGORY: SETTINGS + // OS: O + SETTINGS_NETWORK_SCORER = 861; + + // OPEN: Settings > About device > Model > Hardware info dialog + DIALOG_SETTINGS_HARDWARE_INFO = 862; + + // OPEN: Settings > Security & screen lock -> Lock screen preferences + // CATEGORY: SETTINGS + SETTINGS_LOCK_SCREEN_PREFERENCES = 882; + + + // OPEN: Settings -> Display -> When in VR Mode + VR_DISPLAY_PREFERENCE = 921; + + // OPEN: Settings > Accessibility > Magnification + // CATEGORY: SETTINGS + // OS: O + ACCESSIBILITY_SCREEN_MAGNIFICATION_SETTINGS = 922; + + // OPEN: Settings -> System -> Reset options + RESET_DASHBOARD = 924; + + // OPEN: Settings > Security > Nexus Imprint > [Fingerprint] > Delete + // CATEGORY: SETTINGS + // OS: O + FINGERPRINT_REMOVE_SIDECAR = 934; + + // OPEN: Settings > Storage > Movies & TV + // CATEGORY: SETTINGS + // OS: O + APPLICATIONS_STORAGE_MOVIES = 935; + + // OPEN: Settings > Security > Managed Device Info > Apps installed + // CATEGORY: SETTINGS + // OS: O + ENTERPRISE_PRIVACY_INSTALLED_APPS = 938; + + // OPEN: Settings > Security > Managed Device Info > nnn permissions + // CATEGORY: SETTINGS + // OS: O + ENTERPRISE_PRIVACY_PERMISSIONS = 939; + + + // OPEN: Settings > Security > Managed Device Info > Default apps + // CATEGORY: SETTINGS + // OS: O + ENTERPRISE_PRIVACY_DEFAULT_APPS = 940; + + // OPEN: Choose screen lock dialog in Settings + // CATEGORY: SETTINGS + // OS: O DR + SETTINGS_CHOOSE_LOCK_DIALOG = 990; + + // OPEN: Settings > System > Languages & input > Assist gesture + // CATEGORY: SETTINGS + // OS: O DR + SETTINGS_ASSIST_GESTURE = 996; + // OPEN: Settings > Connected Devices > Bluetooth > (click on details link for a paired device) BLUETOOTH_DEVICE_DETAILS = 1009; + // OPEN: Settings > credential pages - prompt for key guard configuration confirmation + CONFIGURE_KEYGUARD_DIALOG = 1010; + + // OPEN: Settings > Network > Tether > Wi-Fi hotspot + WIFI_TETHER_SETTINGS = 1014; + + // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device) + // -> Edit name button. + // CATEGORY: SETTINGS + // OS: O DR + DIALOG_BLUETOOTH_PAIRED_DEVICE_RENAME = 1015; + // OPEN: Settings > Connected devices > Bluetooth > Pair new device + // CATEGORY: SETTINGS + // OS: O DR BLUETOOTH_PAIRING = 1018; + // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device) + // -> Forget button. + // CATEGORY: SETTINGS + // OS: O DR + DIALOG_BLUETOOTH_PAIRED_DEVICE_FORGET = 1031; + + // OPEN: Settings > Storage > Photos & Videos + // CATEGORY: SETTINGS + // OS: O MR + APPLICATIONS_STORAGE_PHOTOS = 1092; + + // OPEN: Settings > Display > Colors + // CATEGORY: SETTINGS + // OS: O MR + COLOR_MODE_SETTINGS = 1143; + + // OPEN: Settings > Developer Options > Experiment dashboard + // CATEGORY: SETTINGS + SETTINGS_FEATURE_FLAGS_DASHBOARD = 1217; + + // OPEN: Settings > Notifications > [App] > Topic Notifications + // CATEGORY: SETTINGS + // OS: P + NOTIFICATION_CHANNEL_GROUP = 1218; + + // OPEN: Settings > Developer options > Enable > Info dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1219; + + // OPEN: Settings > Developer options > OEM unlocking > Info dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_ENABLE_OEM_UNLOCKING = 1220; + + // OPEN: Settings > Developer options > USB debugging > Info dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_ENABLE_ADB = 1222; + + // OPEN: Settings > Security > Nexus Imprint > [Fingerprint] + // CATEGORY: SETTINGS + // OS: P + FINGERPRINT_AUTHENTICATE_SIDECAR = 1221; + + // OPEN: Settings > Developer options > Revoke USB debugging authorizations > Info dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_CLEAR_ADB_KEYS = 1223; + + // Open: Settings > Developer options > Quick setting tile config + // CATEGORY: SETTINGS + // OS: P + DEVELOPMENT_QS_TILE_CONFIG = 1224; + + // OPEN: Settings > Developer options > Store logger data persistently on device > Info dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_LOG_PERSIST = 1225; + + // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling + // CATEGORY: SETTINGS + // OS: P + WIFI_CALLING_FOR_SUB = 1230; + + // Open: Settings > Dev options > Oem unlock > lock it > warning dialog. + // OS: P + DIALOG_OEM_LOCK_INFO = 1238; + + // Open: Settings > System > About phone > IMEI + // CATEGORY: SETTINGS + // OS: P + DIALOG_IMEI_INFO = 1240; + + // OPEN: Settings > System > About Phone > Sim status + // CATEGORY: SETTINGS + // OS: P + DIALOG_SIM_STATUS = 1246; + + // OPEN: Settings > System > About Phone > Android Version + // CATEGORY: SETTINGS + // OS: P + DIALOG_FIRMWARE_VERSION = 1247; + + // OPEN: Settings > Battery(version 2) + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_POWER_USAGE_SUMMARY_V2 = 1263; + + // OPEN: Settings > Connected devices > Connection preferences + // CATEGORY: SETTINGS + // OS: P + CONNECTION_DEVICE_ADVANCED = 1264; + + // OPEN: Settings > Security > Screen lock gear icon + // CATEGORY: SETTINGS + // OS: P + SCREEN_LOCK_SETTINGS = 1265; + + // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Delete rule (trash can icon) + // CATEGORY: SETTINGS + // OS: P + NOTIFICATION_ZEN_MODE_DELETE_RULE_DIALOG = 1266; + + // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule > Event/Time + // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name + // CATEGORY: SETTINGS + // OS: P + NOTIFICATION_ZEN_MODE_RULE_NAME_DIALOG = 1269; + + // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule + // CATEGORY: SETTINGS + // OS: P + NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG = 1270; + + // OPEN: Settings > Battery > Smart Battery + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_SMART_BATTERY = 1281; + + // OPEN: Settings > Battery > Smart Battery > Restricted apps + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_RESTRICTED_APP_DETAILS = 1285; + + // OPEN: Settings > Sound & notification > Do Not Disturb > Turn on now + // CATEGORY: SETTINGS + // OS: P + NOTIFICATION_ZEN_MODE_ENABLE_DIALOG = 1286; + + // OPEN: Settings->Connected Devices->USB->(click on details link) + // CATEGORY: SETTINGS + // OS: P + USB_DEVICE_DETAILS = 1291; + + // OPEN: Settings > Accessibility > Vibration + // CATEGORY: SETTINGS + // OS: P + ACCESSIBILITY_VIBRATION = 1292; + + // OPEN: Settings > Accessibility > Vibration > Notification vibration + // CATEGORY: SETTINGS + // OS: P + ACCESSIBILITY_VIBRATION_NOTIFICATION = 1293; + + // OPEN: Settings > Accessibility > Vibration > Touch vibration + // CATEGORY: SETTINGS + // OS: P + ACCESSIBILITY_VIBRATION_TOUCH = 1294; + + // OPEN: Settings->Developer Options->Default USB + // CATEGORY: SETTINGS + // OS: P + USB_DEFAULT = 1312; + + // OPEN: Settings > Battery > Battery tip > Battery tip Dialog + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_BATTERY_TIP_DIALOG = 1323; + + // OPEN: DND Settings > What to block + // OS: P + ZEN_WHAT_TO_BLOCK = 1339; + + // OPEN: Settings > Sounds > Do Not Disturb > Duration + // CATEGORY: SETTINGS + // OS: P + NOTIFICATION_ZEN_MODE_DURATION_DIALOG = 1341; + + // OPEN: Settings > Date & time > Select time zone -> Region + // CATEGORY: SETTINGS + // OS: P + SETTINGS_ZONE_PICKER_REGION = 1355; + + // OPEN: Settings > Date & time > Select time zone -> Time Zone + // CATEGORY: SETTINGS + // OS: P + SETTINGS_ZONE_PICKER_TIME_ZONE = 1356; + // OPEN: Settings > Date & time > Select time zone -> Select UTC Offset + // CATEGORY: SETTINGS + // OS: P + SETTINGS_ZONE_PICKER_FIXED_OFFSET = 1357; + + // OPEN: Settings > Gestures > Prevent Ringing + // OS: P + SETTINGS_PREVENT_RINGING = 1360; + + // Settings > Condition > Device muted + // CATEGORY: SETTINGS + // OS: P + SETTINGS_CONDITION_DEVICE_MUTED = 1368; + + // Settings > Condition > Device vibrate + // CATEGORY: SETTINGS + // OS: P + SETTINGS_CONDITION_DEVICE_VIBRATE = 1369; + + // OPEN: Settings > Connected devices > previously connected devices + // CATEGORY: SETTINGS + // OS: P + PREVIOUSLY_CONNECTED_DEVICES = 1370; + + // OPEN: Settings > Network & Internet > Wi-Fi > Wi-Fi Preferences > Turn on Wi-Fi automatically + // note: Wifi Scanning must be off for this dialog to show + // CATEGORY: SETTINGS + // OS: P + WIFI_SCANNING_NEEDED_DIALOG = 1373; + + // OPEN: Settings > System > Gestures > Swipe up gesture + // CATEGORY: SETTINGS + // OS: P + SETTINGS_GESTURE_SWIPE_UP = 1374; + + // OPEN: Settings > Storage > Dialog to format a storage volume + // CATEGORY: SETTINGS + // OS: P + DIALOG_VOLUME_FORMAT = 1375; + + // OPEN: DND onboarding activity + // CATEGORY: SETTINGS + // OS: P + SETTINGS_ZEN_ONBOARDING = 1380; + + // OPEN: Settings > Display > Auto brightness + // CATEGORY: SETTINGS + // OS: P + SETTINGS_AUTO_BRIGHTNESS = 1381; + + // OPEN: Settings > Connected Devices > Bluetooth + // CATEGORY: SETTINGS + // OS: P + BLUETOOTH_FRAGMENT = 1390; + + // Screen: DND Settings > Notifications + // OS: P + SETTINGS_ZEN_NOTIFICATIONS = 1400; + + // An event category for slices. + // OPEN: Slice became visible. + // CLOSE: Slice became invisible. + // ACTION: Slice was tapped. + SLICE = 1401; + + // OPEN: Settings -> Developer Options -> Disable Bluetooth A2DP hardware + // offload + // CATEGORY: SETTINGS + // OS: P + DIALOG_BLUETOOTH_DISABLE_A2DP_HW_OFFLOAD = 1441; + // OPEN: Settings homepage SETTINGS_HOMEPAGE = 1502; + // OPEN: Settings > Create shortcut(widget) + // CATEGORY: SETTINGS + // OS: Q + SETTINGS_CREATE_SHORTCUT = 1503; + + // OPEN: Face Enroll introduction + // CATEGORY: SETTINGS + // OS: Q + FACE_ENROLL_INTRO = 1506; + + // OPEN: Face Enroll introduction + // CATEGORY: SETTINGS + // OS: Q + FACE_ENROLL_ENROLLING = 1507; + + // OPEN: Face Enroll introduction + // CATEGORY: SETTINGS + // OS: Q + FACE_ENROLL_FINISHED = 1508; + + // OPEN: Face Enroll sidecar + // CATEGORY: SETTINGS + // OS: Q + FACE_ENROLL_SIDECAR = 1509; + + // OPEN: Settings > Add face > Error dialog + // OS: Q + DIALOG_FACE_ERROR = 1510; + + // OPEN: Settings > Security > Face + // CATEGORY: SETTINGS + // OS: Q + FACE = 1511; + + // OPEN: Settings > Acessibility > HearingAid pairing instructions dialog + // CATEGORY: SETTINGS + // OS: Q + DIALOG_ACCESSIBILITY_HEARINGAID = 1512; + + // OPEN: Settings > Add face + // OS: Q + FACE_ENROLL_PREVIEW = 1554; + + // OPEN: Settings > Network & Internet > Wi-Fi > Add network + // CATEGORY: SETTINGS + // OS: Q + SETTINGS_WIFI_ADD_NETWORK = 1556; + + // OPEN: Settings > System > Input & Gesture > Reach up gesture + // OS: Q + SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557; + // OPEN: Settings > System > Input & Gesture > Wake screen SETTINGS_GESTURE_WAKE_SCREEN = 1570; @@ -80,6 +2079,19 @@ enum PageId { // OPEN: Settings > Privacy TOP_LEVEL_PRIVACY = 1587; + // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions > + // Allow apps to override + // CATEGORY: SETTINGS + // OS: Q + NOTIFICATION_ZEN_MODE_OVERRIDING_APPS = 1588; + + + // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions > + // Allow apps to override > Choose app + // CATEGORY: SETTINGS + // OS: Q + NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589; + // OPEN: Settings > Developer options > Disable > Info dialog DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591; @@ -91,4 +2103,76 @@ enum PageId { // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597; + + + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_SETTINGS = 1604; + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior > Custom + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_SOUND_SETTINGS = 1605; + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior > Use default Do Not Disturb behavior + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_DEFAULT_SETTINGS = 1606; + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior > Use default Do Not Disturb behavior + // > Notification restriction + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_NOTIFICATION_RESTRICTIONS = 1608; + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior > Use default Do Not Disturb behavior + // > Notification restriction > Custom + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_VIS_EFFECTS = 1609; + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior > Use default Do Not Disturb behavior + // > Notification restriction > Custom > Allow messages + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_MESSAGES = 1610; + + // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) + // > Do Not Disturb behavior > Use default Do Not Disturb behavior + // > Notification restriction > Custom > Allow calls + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_RULE_CALLS = 1611; + + // OPEN: Settings > Sound > Do Not Disturb > Click footer link if custom settings applied + // CATEGORY: SETTINGS + // OS: Q + ZEN_CUSTOM_SETTINGS_DIALOG = 1612; + + // OPEN: Settings > Developer Options > Game Update Packages + // CATEGORY: SETTINGS + // OS: Q + SETTINGS_GUP_DASHBOARD = 1613; + + // OPEN: Settings > Accessibility > Vibration > Ring vibration + // CATEGORY: SETTINGS + // OS: Q + ACCESSIBILITY_VIBRATION_RING = 1620; + + // OPEN: Settings > System > Input & Gesture > Skip songs + SETTINGS_GESTURE_SKIP = 1624; + + // OPEN: Settings > System > Input & Gesture > Silence alerts + SETTINGS_GESTURE_SILENCE = 1625; + + // OPEN: Settings > System > Input & Gesture > Tap to check + SETTINGS_GESTURE_TAP_SCREEN = 1626; } diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto index d0c922664351..76c240ecff4d 100644 --- a/core/proto/android/bluetooth/enums.proto +++ b/core/proto/android/bluetooth/enums.proto @@ -41,3 +41,18 @@ enum EnableDisableReasonEnum { ENABLE_DISABLE_REASON_USER_SWITCH = 8; ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9; } + +enum DirectionEnum { + DIRECTION_UNKNOWN = 0; + DIRECTION_OUTGOING = 1; + DIRECTION_INCOMING = 2; +} + +// First item is the default value, other values follow Bluetooth spec definition +enum LinkTypeEnum { + // Link type is at most 1 byte (0xFF), thus 0xFFF must not be a valid value + LINK_TYPE_UNKNOWN = 0xFFF; + LINK_TYPE_SCO = 0x00; + LINK_TYPE_ACL = 0x01; + LINK_TYPE_ESCO = 0x02; +} diff --git a/core/proto/android/bluetooth/hci/enums.proto b/core/proto/android/bluetooth/hci/enums.proto new file mode 100644 index 000000000000..e1d96bbce68a --- /dev/null +++ b/core/proto/android/bluetooth/hci/enums.proto @@ -0,0 +1,519 @@ +/* + * 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. + */ + +syntax = "proto2"; +package android.bluetooth.hci; + +option java_outer_classname = "BluetoothHciProtoEnums"; +option java_multiple_files = true; + +// HCI command opcodes (OCF+OGF) from Bluetooth 5.0 specification Vol 2, Part E, Section 7 +// Original definition: system/bt/stack/include/hcidefs.h +enum CommandEnum { + // Opcode is at most 2 bytes (0xFFFF), thus 0xFFFFF must not be a valid value + CMD_UNKNOWN = 0xFFFFF; + // Link control commands 0x0400 + CMD_INQUIRY = 0x0401; + CMD_INQUIRY_CANCEL = 0x0402; + CMD_PERIODIC_INQUIRY_MODE = 0x0403; + CMD_EXIT_PERIODIC_INQUIRY_MODE = 0x0404; + CMD_CREATE_CONNECTION = 0x0405; + CMD_DISCONNECT = 0x0406; + CMD_ADD_SCO_CONNECTION = 0x0407; // Deprecated since Bluetooth 1.2 + CMD_CREATE_CONNECTION_CANCEL = 0x0408; + CMD_ACCEPT_CONNECTION_REQUEST = 0x0409; + CMD_REJECT_CONNECTION_REQUEST = 0x040A; + CMD_LINK_KEY_REQUEST_REPLY = 0x040B; + CMD_LINK_KEY_REQUEST_NEG_REPLY = 0x040C; + CMD_PIN_CODE_REQUEST_REPLY = 0x040D; + CMD_PIN_CODE_REQUEST_NEG_REPLY = 0x040E; + CMD_CHANGE_CONN_PACKET_TYPE = 0x040F; + CMD_AUTHENTICATION_REQUESTED = 0x0411; + CMD_SET_CONN_ENCRYPTION = 0x0413; + CMD_CHANGE_CONN_LINK_KEY = 0x0415; + CMD_MASTER_LINK_KEY = 0x0417; + CMD_RMT_NAME_REQUEST = 0x0419; + CMD_RMT_NAME_REQUEST_CANCEL = 0x041A; + CMD_READ_RMT_FEATURES = 0x041B; + CMD_READ_RMT_EXT_FEATURES = 0x041C; + CMD_READ_RMT_VERSION_INFO = 0x041D; + CMD_READ_RMT_CLOCK_OFFSET = 0x041F; + CMD_READ_LMP_HANDLE = 0x0420; + CMD_SETUP_ESCO_CONNECTION = 0x0428; + CMD_ACCEPT_ESCO_CONNECTION = 0x0429; + CMD_REJECT_ESCO_CONNECTION = 0x042A; + CMD_IO_CAPABILITY_REQUEST_REPLY = 0x042B; + CMD_USER_CONF_REQUEST_REPLY = 0x042C; + CMD_USER_CONF_VALUE_NEG_REPLY = 0x042D; + CMD_USER_PASSKEY_REQ_REPLY = 0x042E; + CMD_USER_PASSKEY_REQ_NEG_REPLY = 0x042F; + CMD_REM_OOB_DATA_REQ_REPLY = 0x0430; + CMD_REM_OOB_DATA_REQ_NEG_REPLY = 0x0433; + CMD_IO_CAP_REQ_NEG_REPLY = 0x0434; + // BEGIN: AMP commands (not used in system/bt) + CMD_CREATE_PHYSICAL_LINK = 0x0435; + CMD_ACCEPT_PHYSICAL_LINK = 0x0436; + CMD_DISCONNECT_PHYSICAL_LINK = 0x0437; + CMD_CREATE_LOGICAL_LINK = 0x0438; + CMD_ACCEPT_LOGICAL_LINK = 0x0439; + CMD_DISCONNECT_LOGICAL_LINK = 0x043A; + CMD_LOGICAL_LINK_CANCEL = 0x043B; + CMD_FLOW_SPEC_MODIFY = 0x043C; + // END: AMP commands + CMD_ENH_SETUP_ESCO_CONNECTION = 0x043D; + CMD_ENH_ACCEPT_ESCO_CONNECTION = 0x043E; + CMD_TRUNCATED_PAGE = 0x043F; + CMD_TRUNCATED_PAGE_CANCEL = 0x0440; + CMD_SET_CLB = 0x0441; + CMD_RECEIVE_CLB = 0x0442; + CMD_START_SYNC_TRAIN = 0x0443; + CMD_RECEIVE_SYNC_TRAIN = 0x0444; + CMD_REM_OOB_EXTENDED_DATA_REQ_REPLY = 0x0445; // Not currently used in system/bt + // Link policy commands 0x0800 + CMD_HOLD_MODE = 0x0801; + CMD_SNIFF_MODE = 0x0803; + CMD_EXIT_SNIFF_MODE = 0x0804; + CMD_PARK_MODE = 0x0805; + CMD_EXIT_PARK_MODE = 0x0806; + CMD_QOS_SETUP = 0x0807; + CMD_ROLE_DISCOVERY = 0x0809; + CMD_SWITCH_ROLE = 0x080B; + CMD_READ_POLICY_SETTINGS = 0x080C; + CMD_WRITE_POLICY_SETTINGS = 0x080D; + CMD_READ_DEF_POLICY_SETTINGS = 0x080E; + CMD_WRITE_DEF_POLICY_SETTINGS = 0x080F; + CMD_FLOW_SPECIFICATION = 0x0810; + CMD_SNIFF_SUB_RATE = 0x0811; + // Host controller baseband commands 0x0C00 + CMD_SET_EVENT_MASK = 0x0C01; + CMD_RESET = 0x0C03; + CMD_SET_EVENT_FILTER = 0x0C05; + CMD_FLUSH = 0x0C08; + CMD_READ_PIN_TYPE = 0x0C09; + CMD_WRITE_PIN_TYPE = 0x0C0A; + CMD_CREATE_NEW_UNIT_KEY = 0x0C0B; + CMD_GET_MWS_TRANS_LAYER_CFG = 0x0C0C; // Deprecated (not used in spec) + CMD_READ_STORED_LINK_KEY = 0x0C0D; + CMD_WRITE_STORED_LINK_KEY = 0x0C11; + CMD_DELETE_STORED_LINK_KEY = 0x0C12; + CMD_CHANGE_LOCAL_NAME = 0x0C13; + CMD_READ_LOCAL_NAME = 0x0C14; + CMD_READ_CONN_ACCEPT_TOUT = 0x0C15; + CMD_WRITE_CONN_ACCEPT_TOUT = 0x0C16; + CMD_READ_PAGE_TOUT = 0x0C17; + CMD_WRITE_PAGE_TOUT = 0x0C18; + CMD_READ_SCAN_ENABLE = 0x0C19; + CMD_WRITE_SCAN_ENABLE = 0x0C1A; + CMD_READ_PAGESCAN_CFG = 0x0C1B; + CMD_WRITE_PAGESCAN_CFG = 0x0C1C; + CMD_READ_INQUIRYSCAN_CFG = 0x0C1D; + CMD_WRITE_INQUIRYSCAN_CFG = 0x0C1E; + CMD_READ_AUTHENTICATION_ENABLE = 0x0C1F; + CMD_WRITE_AUTHENTICATION_ENABLE = 0x0C20; + CMD_READ_ENCRYPTION_MODE = 0x0C21; // Deprecated + CMD_WRITE_ENCRYPTION_MODE = 0x0C22; // Deprecated + CMD_READ_CLASS_OF_DEVICE = 0x0C23; + CMD_WRITE_CLASS_OF_DEVICE = 0x0C24; + CMD_READ_VOICE_SETTINGS = 0x0C25; + CMD_WRITE_VOICE_SETTINGS = 0x0C26; + CMD_READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27; + CMD_WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28; + CMD_READ_NUM_BCAST_REXMITS = 0x0C29; + CMD_WRITE_NUM_BCAST_REXMITS = 0x0C2A; + CMD_READ_HOLD_MODE_ACTIVITY = 0x0C2B; + CMD_WRITE_HOLD_MODE_ACTIVITY = 0x0C2C; + CMD_READ_TRANSMIT_POWER_LEVEL = 0x0C2D; + CMD_READ_SCO_FLOW_CTRL_ENABLE = 0x0C2E; + CMD_WRITE_SCO_FLOW_CTRL_ENABLE = 0x0C2F; + CMD_SET_HC_TO_HOST_FLOW_CTRL = 0x0C31; + CMD_HOST_BUFFER_SIZE = 0x0C33; + CMD_HOST_NUM_PACKETS_DONE = 0x0C35; + CMD_READ_LINK_SUPER_TOUT = 0x0C36; + CMD_WRITE_LINK_SUPER_TOUT = 0x0C37; + CMD_READ_NUM_SUPPORTED_IAC = 0x0C38; + CMD_READ_CURRENT_IAC_LAP = 0x0C39; + CMD_WRITE_CURRENT_IAC_LAP = 0x0C3A; + CMD_READ_PAGESCAN_PERIOD_MODE = 0x0C3B; // Deprecated + CMD_WRITE_PAGESCAN_PERIOD_MODE = 0x0C3C; // Deprecated + CMD_READ_PAGESCAN_MODE = 0x0C3D; // Deprecated + CMD_WRITE_PAGESCAN_MODE = 0x0C3E; // Deprecated + CMD_SET_AFH_CHANNELS = 0x0C3F; + CMD_READ_INQSCAN_TYPE = 0x0C42; + CMD_WRITE_INQSCAN_TYPE = 0x0C43; + CMD_READ_INQUIRY_MODE = 0x0C44; + CMD_WRITE_INQUIRY_MODE = 0x0C45; + CMD_READ_PAGESCAN_TYPE = 0x0C46; + CMD_WRITE_PAGESCAN_TYPE = 0x0C47; + CMD_READ_AFH_ASSESSMENT_MODE = 0x0C48; + CMD_WRITE_AFH_ASSESSMENT_MODE = 0x0C49; + CMD_READ_EXT_INQ_RESPONSE = 0x0C51; + CMD_WRITE_EXT_INQ_RESPONSE = 0x0C52; + CMD_REFRESH_ENCRYPTION_KEY = 0x0C53; + CMD_READ_SIMPLE_PAIRING_MODE = 0x0C55; + CMD_WRITE_SIMPLE_PAIRING_MODE = 0x0C56; + CMD_READ_LOCAL_OOB_DATA = 0x0C57; + CMD_READ_INQ_TX_POWER_LEVEL = 0x0C58; + CMD_WRITE_INQ_TX_POWER_LEVEL = 0x0C59; + CMD_READ_ERRONEOUS_DATA_RPT = 0x0C5A; + CMD_WRITE_ERRONEOUS_DATA_RPT = 0x0C5B; + CMD_ENHANCED_FLUSH = 0x0C5F; + CMD_SEND_KEYPRESS_NOTIF = 0x0C60; + CMD_READ_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C61; + CMD_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C62; + CMD_SET_EVENT_MASK_PAGE_2 = 0x0C63; + CMD_READ_LOCATION_DATA = 0x0C64; + CMD_WRITE_LOCATION_DATA = 0x0C65; + CMD_READ_FLOW_CONTROL_MODE = 0x0C66; + CMD_WRITE_FLOW_CONTROL_MODE = 0x0C67; + CMD_READ_ENHANCED_TX_PWR_LEVEL = 0x0C68; // Not currently used in system/bt + CMD_READ_BE_FLUSH_TOUT = 0x0C69; + CMD_WRITE_BE_FLUSH_TOUT = 0x0C6A; + CMD_SHORT_RANGE_MODE = 0x0C6B; + CMD_READ_BLE_HOST_SUPPORT = 0x0C6C; + CMD_WRITE_BLE_HOST_SUPPORT = 0x0C6D; + CMD_SET_MWS_CHANNEL_PARAMETERS = 0x0C6E; + CMD_SET_EXTERNAL_FRAME_CONFIGURATION = 0x0C6F; + CMD_SET_MWS_SIGNALING = 0x0C70; + CMD_SET_MWS_TRANSPORT_LAYER = 0x0C71; + CMD_SET_MWS_SCAN_FREQUENCY_TABLE = 0x0C72; + CMD_SET_MWS_PATTERN_CONFIGURATION = 0x0C73; + CMD_SET_RESERVED_LT_ADDR = 0x0C74; + CMD_DELETE_RESERVED_LT_ADDR = 0x0C75; + CMD_WRITE_CLB_DATA = 0x0C76; + CMD_READ_SYNC_TRAIN_PARAM = 0x0C77; + CMD_WRITE_SYNC_TRAIN_PARAM = 0x0C78; + CMD_READ_SECURE_CONNS_SUPPORT = 0x0C79; + CMD_WRITE_SECURE_CONNS_SUPPORT = 0x0C7A; + CMD_READ_AUTHED_PAYLOAD_TIMEOUT = 0x0C7B; // Not currently used in system/bt + CMD_WRITE_AUTHED_PAYLOAD_TIMEOUT = 0x0C7C; // Not currently used in system/bt + CMD_READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D; // Not currently used in system/bt + CMD_READ_EXTENDED_PAGE_TIMEOUT = 0x0C7E; // Not currently used in system/bt + CMD_WRITE_EXTENDED_PAGE_TIMEOUT = 0x0C7F; // Not currently used in system/bt + CMD_READ_EXTENDED_INQUIRY_LENGTH = 0x0C80; // Not currently used in system/bt + CMD_WRITE_EXTENDED_INQUIRY_LENGTH = 0x0C81; // Not currently used in system/bt + // Informational parameter commands 0x1000 + CMD_READ_LOCAL_VERSION_INFO = 0x1001; + CMD_READ_LOCAL_SUPPORTED_CMDS = 0x1002; + CMD_READ_LOCAL_FEATURES = 0x1003; + CMD_READ_LOCAL_EXT_FEATURES = 0x1004; + CMD_READ_BUFFER_SIZE = 0x1005; + CMD_READ_COUNTRY_CODE = 0x1007; // Deprecated + CMD_READ_BD_ADDR = 0x1009; + CMD_READ_DATA_BLOCK_SIZE = 0x100A; + CMD_READ_LOCAL_SUPPORTED_CODECS = 0x100B; + // Status parameter commands 0x1400 + CMD_READ_FAILED_CONTACT_COUNTER = 0x1401; + CMD_RESET_FAILED_CONTACT_COUNTER = 0x1402; + CMD_GET_LINK_QUALITY = 0x1403; + CMD_READ_RSSI = 0x1405; + CMD_READ_AFH_CH_MAP = 0x1406; + CMD_READ_CLOCK = 0x1407; + CMD_READ_ENCR_KEY_SIZE = 0x1408; + CMD_READ_LOCAL_AMP_INFO = 0x1409; + CMD_READ_LOCAL_AMP_ASSOC = 0x140A; + CMD_WRITE_REMOTE_AMP_ASSOC = 0x140B; + CMD_GET_MWS_TRANSPORT_CFG = 0x140C; // Not currently used in system/bt + CMD_SET_TRIGGERED_CLK_CAPTURE = 0x140D; // Not currently used in system/bt + // Testing commands 0x1800 + CMD_READ_LOOPBACK_MODE = 0x1801; + CMD_WRITE_LOOPBACK_MODE = 0x1802; + CMD_ENABLE_DEV_UNDER_TEST_MODE = 0x1803; + CMD_WRITE_SIMP_PAIR_DEBUG_MODE = 0x1804; + CMD_ENABLE_AMP_RCVR_REPORTS = 0x1807; + CMD_AMP_TEST_END = 0x1808; + CMD_AMP_TEST = 0x1809; + CMD_WRITE_SECURE_CONN_TEST_MODE = 0x180A; // Not currently used in system/bt + // BLE commands 0x2000 + CMD_BLE_SET_EVENT_MASK = 0x2001; + CMD_BLE_READ_BUFFER_SIZE = 0x2002; + CMD_BLE_READ_LOCAL_SPT_FEAT = 0x2003; + CMD_BLE_WRITE_LOCAL_SPT_FEAT = 0x2004; + CMD_BLE_WRITE_RANDOM_ADDR = 0x2005; + CMD_BLE_WRITE_ADV_PARAMS = 0x2006; + CMD_BLE_READ_ADV_CHNL_TX_POWER = 0x2007; + CMD_BLE_WRITE_ADV_DATA = 0x2008; + CMD_BLE_WRITE_SCAN_RSP_DATA = 0x2009; + CMD_BLE_WRITE_ADV_ENABLE = 0x200A; + CMD_BLE_WRITE_SCAN_PARAMS = 0x200B; + CMD_BLE_WRITE_SCAN_ENABLE = 0x200C; + CMD_BLE_CREATE_LL_CONN = 0x200D; + CMD_BLE_CREATE_CONN_CANCEL = 0x200E; + CMD_BLE_READ_WHITE_LIST_SIZE = 0x200F; + CMD_BLE_CLEAR_WHITE_LIST = 0x2010; + CMD_BLE_ADD_WHITE_LIST = 0x2011; + CMD_BLE_REMOVE_WHITE_LIST = 0x2012; + CMD_BLE_UPD_LL_CONN_PARAMS = 0x2013; + CMD_BLE_SET_HOST_CHNL_CLASS = 0x2014; + CMD_BLE_READ_CHNL_MAP = 0x2015; + CMD_BLE_READ_REMOTE_FEAT = 0x2016; + CMD_BLE_ENCRYPT = 0x2017; + CMD_BLE_RAND = 0x2018; + CMD_BLE_START_ENC = 0x2019; + CMD_BLE_LTK_REQ_REPLY = 0x201A; + CMD_BLE_LTK_REQ_NEG_REPLY = 0x201B; + CMD_BLE_READ_SUPPORTED_STATES = 0x201C; + CMD_BLE_RECEIVER_TEST = 0x201D; + CMD_BLE_TRANSMITTER_TEST = 0x201E; + CMD_BLE_TEST_END = 0x201F; + CMD_BLE_RC_PARAM_REQ_REPLY = 0x2020; + CMD_BLE_RC_PARAM_REQ_NEG_REPLY = 0x2021; + CMD_BLE_SET_DATA_LENGTH = 0x2022; + CMD_BLE_READ_DEFAULT_DATA_LENGTH = 0x2023; + CMD_BLE_WRITE_DEFAULT_DATA_LENGTH = 0x2024; + CMD_BLE_GENERATE_DHKEY = 0x2026; // Not currently used in system/bt + CMD_BLE_ADD_DEV_RESOLVING_LIST = 0x2027; + CMD_BLE_RM_DEV_RESOLVING_LIST = 0x2028; + CMD_BLE_CLEAR_RESOLVING_LIST = 0x2029; + CMD_BLE_READ_RESOLVING_LIST_SIZE = 0x202A; + CMD_BLE_READ_RESOLVABLE_ADDR_PEER = 0x202B; + CMD_BLE_READ_RESOLVABLE_ADDR_LOCAL = 0x202C; + CMD_BLE_SET_ADDR_RESOLUTION_ENABLE = 0x202D; + CMD_BLE_SET_RAND_PRIV_ADDR_TIMOUT = 0x202E; + CMD_BLE_READ_MAXIMUM_DATA_LENGTH = 0x202F; + CMD_BLE_READ_PHY = 0x2030; + CMD_BLE_SET_DEFAULT_PHY = 0x2031; + CMD_BLE_SET_PHY = 0x2032; + CMD_BLE_ENH_RECEIVER_TEST = 0x2033; + CMD_BLE_ENH_TRANSMITTER_TEST = 0x2034; + CMD_BLE_SET_EXT_ADVERTISING_RANDOM_ADDRESS = 0x2035; + CMD_BLE_SET_EXT_ADVERTISING_PARAM = 0x2036; + CMD_BLE_SET_EXT_ADVERTISING_DATA = 0x2037; + CMD_BLE_SET_EXT_ADVERTISING_SCAN_RESP = 0x2038; + CMD_BLE_SET_EXT_ADVERTISING_ENABLE = 0x2039; + CMD_BLE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A; + CMD_BLE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B; + CMD_BLE_REMOVE_ADVERTISING_SET = 0x203C; + CMD_BLE_CLEAR_ADVERTISING_SETS = 0x203D; + CMD_BLE_SET_PERIODIC_ADVERTISING_PARAM = 0x203E; + CMD_BLE_SET_PERIODIC_ADVERTISING_DATA = 0x203F; + CMD_BLE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040; + CMD_BLE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041; + CMD_BLE_SET_EXTENDED_SCAN_ENABLE = 0x2042; + CMD_BLE_EXTENDED_CREATE_CONNECTION = 0x2043; + CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044; + CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045; + CMD_BLE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046; + CMD_BLE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 0x2047; + CMD_BLE_RM_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 0x2048; + CMD_BLE_CLEAR_PERIODIC_ADVERTISING_LIST = 0x2049; + CMD_BLE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 0x204A; + CMD_BLE_READ_TRANSMIT_POWER = 0x204B; + CMD_BLE_READ_RF_COMPENS_POWER = 0x204C; + CMD_BLE_WRITE_RF_COMPENS_POWER = 0x204D; + CMD_BLE_SET_PRIVACY_MODE = 0x204E; + // Vendor specific commands 0xFC00 and above + // Android vendor specific commands defined in + // https://source.android.com/devices/bluetooth/hci_requirements#vendor-specific-capabilities + CMD_BLE_VENDOR_CAP = 0xFD53; + CMD_BLE_MULTI_ADV = 0xFD54; + CMD_BLE_BATCH_SCAN = 0xFD56; + CMD_BLE_ADV_FILTER = 0xFD57; + CMD_BLE_TRACK_ADV = 0xFD58; + CMD_BLE_ENERGY_INFO = 0xFD59; + CMD_BLE_EXTENDED_SCAN_PARAMS = 0xFD5A; + CMD_CONTROLLER_DEBUG_INFO = 0xFD5B; + CMD_CONTROLLER_A2DP_OPCODE = 0xFD5D; + CMD_BRCM_SET_ACL_PRIORITY = 0xFC57; + // Other vendor specific commands below here +} + +// HCI event codes from the Bluetooth 5.0 specification Vol 2, Part 7, Section 7 +// Original definition: system/bt/stack/include/hcidefs.h +enum EventEnum { + // Event is at most 1 byte (0xFF), thus 0xFFF must not be a valid value + EVT_UNKNOWN = 0xFFF; + EVT_INQUIRY_COMP = 0x01; + EVT_INQUIRY_RESULT = 0x02; + EVT_CONNECTION_COMP = 0x03; + EVT_CONNECTION_REQUEST = 0x04; + EVT_DISCONNECTION_COMP = 0x05; + EVT_AUTHENTICATION_COMP = 0x06; + EVT_RMT_NAME_REQUEST_COMP = 0x07; + EVT_ENCRYPTION_CHANGE = 0x08; + EVT_CHANGE_CONN_LINK_KEY = 0x09; + EVT_MASTER_LINK_KEY_COMP = 0x0A; + EVT_READ_RMT_FEATURES_COMP = 0x0B; + EVT_READ_RMT_VERSION_COMP = 0x0C; + EVT_QOS_SETUP_COMP = 0x0D; + EVT_COMMAND_COMPLETE = 0x0E; + EVT_COMMAND_STATUS = 0x0F; + EVT_HARDWARE_ERROR = 0x10; + EVT_FLUSH_OCCURED = 0x11; + EVT_ROLE_CHANGE = 0x12; + EVT_NUM_COMPL_DATA_PKTS = 0x13; + EVT_MODE_CHANGE = 0x14; + EVT_RETURN_LINK_KEYS = 0x15; + EVT_PIN_CODE_REQUEST = 0x16; + EVT_LINK_KEY_REQUEST = 0x17; + EVT_LINK_KEY_NOTIFICATION = 0x18; + EVT_LOOPBACK_COMMAND = 0x19; + EVT_DATA_BUF_OVERFLOW = 0x1A; + EVT_MAX_SLOTS_CHANGED = 0x1B; + EVT_READ_CLOCK_OFF_COMP = 0x1C; + EVT_CONN_PKT_TYPE_CHANGE = 0x1D; + EVT_QOS_VIOLATION = 0x1E; + EVT_PAGE_SCAN_MODE_CHANGE = 0x1F; // Deprecated + EVT_PAGE_SCAN_REP_MODE_CHNG = 0x20; + EVT_FLOW_SPECIFICATION_COMP = 0x21; + EVT_INQUIRY_RSSI_RESULT = 0x22; + EVT_READ_RMT_EXT_FEATURES_COMP = 0x23; + EVT_ESCO_CONNECTION_COMP = 0x2C; + EVT_ESCO_CONNECTION_CHANGED = 0x2D; + EVT_SNIFF_SUB_RATE = 0x2E; + EVT_EXTENDED_INQUIRY_RESULT = 0x2F; + EVT_ENCRYPTION_KEY_REFRESH_COMP = 0x30; + EVT_IO_CAPABILITY_REQUEST = 0x31; + EVT_IO_CAPABILITY_RESPONSE = 0x32; + EVT_USER_CONFIRMATION_REQUEST = 0x33; + EVT_USER_PASSKEY_REQUEST = 0x34; + EVT_REMOTE_OOB_DATA_REQUEST = 0x35; + EVT_SIMPLE_PAIRING_COMPLETE = 0x36; + EVT_LINK_SUPER_TOUT_CHANGED = 0x38; + EVT_ENHANCED_FLUSH_COMPLETE = 0x39; + EVT_USER_PASSKEY_NOTIFY = 0x3B; + EVT_KEYPRESS_NOTIFY = 0x3C; + EVT_RMT_HOST_SUP_FEAT_NOTIFY = 0x3D; + EVT_BLE_META = 0x3E; + EVT_PHYSICAL_LINK_COMP = 0x40; + EVT_CHANNEL_SELECTED = 0x41; + EVT_DISC_PHYSICAL_LINK_COMP = 0x42; + EVT_PHY_LINK_LOSS_EARLY_WARNING = 0x43; + EVT_PHY_LINK_RECOVERY = 0x44; + EVT_LOGICAL_LINK_COMP = 0x45; + EVT_DISC_LOGICAL_LINK_COMP = 0x46; + EVT_FLOW_SPEC_MODIFY_COMP = 0x47; + EVT_NUM_COMPL_DATA_BLOCKS = 0x48; + EVT_AMP_TEST_START = 0x49; // Not currently used in system/bt + EVT_AMP_TEST_END = 0x4A; // Not currently used in system/bt + EVT_AMP_RECEIVER_RPT = 0x4B; // Not currently used in system/bt + EVT_SHORT_RANGE_MODE_COMPLETE = 0x4C; + EVT_AMP_STATUS_CHANGE = 0x4D; + EVT_SET_TRIGGERED_CLOCK_CAPTURE = 0x4E; + EVT_SYNC_TRAIN_CMPL = 0x4F; // Not currently used in system/bt + EVT_SYNC_TRAIN_RCVD = 0x50; // Not currently used in system/bt + EVT_CONNLESS_SLAVE_BROADCAST_RCVD = 0x51; // Not currently used in system/bt + EVT_CONNLESS_SLAVE_BROADCAST_TIMEOUT = 0x52; // Not currently used in system/bt + EVT_TRUNCATED_PAGE_CMPL = 0x53; // Not currently used in system/bt + EVT_SLAVE_PAGE_RES_TIMEOUT = 0x54; // Not currently used in system/bt + EVT_CONNLESS_SLAVE_BROADCAST_CHNL_MAP_CHANGE = 0x55; // Not currently used in system/bt + EVT_INQUIRY_RES_NOTIFICATION = 0x56; // Not currently used in system/bt + EVT_AUTHED_PAYLOAD_TIMEOUT = 0x57; // Not currently used in system/bt + EVT_SAM_STATUS_CHANGE = 0x58; // Not currently used in system/bt +} + +// Bluetooth low energy related meta event codes +// from the Bluetooth 5.0 specification Vol 2, Part E, Section 7.7.65 +// Original definition: system/bt/stack/include/hcidefs.h +enum BleMetaEventEnum { + // BLE meta event code is at most 1 byte (0xFF), thus 0xFFF must not be a valid value + BLE_EVT_UNKNOWN = 0xFFF; + BLE_EVT_CONN_COMPLETE_EVT = 0x01; + BLE_EVT_ADV_PKT_RPT_EVT = 0x02; + BLE_EVT_LL_CONN_PARAM_UPD_EVT = 0x03; + BLE_EVT_READ_REMOTE_FEAT_CMPL_EVT = 0x04; + BLE_EVT_LTK_REQ_EVT = 0x05; + BLE_EVT_RC_PARAM_REQ_EVT = 0x06; + BLE_EVT_DATA_LENGTH_CHANGE_EVT = 0x07; + BLE_EVT_READ_LOCAL_P256_PUB_KEY = 0x08; // Not currently used in system/bt + BLE_EVT_GEN_DHKEY_CMPL = 0x09; // Not currently used in system/bt + BLE_EVT_ENHANCED_CONN_COMPLETE_EVT = 0x0a; + BLE_EVT_DIRECT_ADV_EVT = 0x0b; + BLE_EVT_PHY_UPDATE_COMPLETE_EVT = 0x0c; + BLE_EVT_EXTENDED_ADVERTISING_REPORT_EVT = 0x0D; + BLE_EVT_PERIODIC_ADV_SYNC_EST_EVT = 0x0E; + BLE_EVT_PERIODIC_ADV_REPORT_EVT = 0x0F; + BLE_EVT_PERIODIC_ADV_SYNC_LOST_EVT = 0x10; + BLE_EVT_SCAN_TIMEOUT_EVT = 0x11; + BLE_EVT_ADVERTISING_SET_TERMINATED_EVT = 0x12; + BLE_EVT_SCAN_REQ_RX_EVT = 0x13; + BLE_EVT_CHNL_SELECTION_ALGORITHM = 0x14; // Not currently used in system/bt +} + +// HCI status code from the Bluetooth 5.0 specification Vol 2, Part D. +// Original definition: system/bt/stack/include/hcidefs.h +enum StatusEnum { + // Status is at most 1 byte (0xFF), thus 0xFFF must not be a valid value + STATUS_UNKNOWN = 0xFFF; + STATUS_SUCCESS = 0x00; + STATUS_ILLEGAL_COMMAND = 0x01; + STATUS_NO_CONNECTION = 0x02; + STATUS_HW_FAILURE = 0x03; + STATUS_PAGE_TIMEOUT = 0x04; + STATUS_AUTH_FAILURE = 0x05; + STATUS_KEY_MISSING = 0x06; + STATUS_MEMORY_FULL = 0x07; + STATUS_CONNECTION_TOUT = 0x08; + STATUS_MAX_NUM_OF_CONNECTIONS = 0x09; + STATUS_MAX_NUM_OF_SCOS = 0x0A; + STATUS_CONNECTION_EXISTS = 0x0B; + STATUS_COMMAND_DISALLOWED = 0x0C; + STATUS_HOST_REJECT_RESOURCES = 0x0D; + STATUS_HOST_REJECT_SECURITY = 0x0E; + STATUS_HOST_REJECT_DEVICE = 0x0F; + STATUS_HOST_TIMEOUT = 0x10; + STATUS_UNSUPPORTED_VALUE = 0x11; + STATUS_ILLEGAL_PARAMETER_FMT = 0x12; + STATUS_PEER_USER = 0x13; + STATUS_PEER_LOW_RESOURCES = 0x14; + STATUS_PEER_POWER_OFF = 0x15; + STATUS_CONN_CAUSE_LOCAL_HOST = 0x16; + STATUS_REPEATED_ATTEMPTS = 0x17; + STATUS_PAIRING_NOT_ALLOWED = 0x18; + STATUS_UNKNOWN_LMP_PDU = 0x19; + STATUS_UNSUPPORTED_REM_FEATURE = 0x1A; + STATUS_SCO_OFFSET_REJECTED = 0x1B; + STATUS_SCO_INTERVAL_REJECTED = 0x1C; + STATUS_SCO_AIR_MODE = 0x1D; + STATUS_INVALID_LMP_PARAM = 0x1E; + STATUS_UNSPECIFIED = 0x1F; + STATUS_UNSUPPORTED_LMP_FEATURE = 0x20; + STATUS_ROLE_CHANGE_NOT_ALLOWED = 0x21; + STATUS_LMP_RESPONSE_TIMEOUT = 0x22; + STATUS_LMP_STATUS_TRANS_COLLISION = 0x23; + STATUS_LMP_PDU_NOT_ALLOWED = 0x24; + STATUS_ENCRY_MODE_NOT_ACCEPTABLE = 0x25; + STATUS_UNIT_KEY_USED = 0x26; + STATUS_QOS_NOT_SUPPORTED = 0x27; + STATUS_INSTANT_PASSED = 0x28; + STATUS_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29; + STATUS_DIFF_TRANSACTION_COLLISION = 0x2A; + STATUS_UNDEFINED_0x2B = 0x2B; // Not used + STATUS_QOS_UNACCEPTABLE_PARAM = 0x2C; + STATUS_QOS_REJECTED = 0x2D; + STATUS_CHAN_CLASSIF_NOT_SUPPORTED = 0x2E; + STATUS_INSUFFCIENT_SECURITY = 0x2F; + STATUS_PARAM_OUT_OF_RANGE = 0x30; + STATUS_UNDEFINED_0x31 = 0x31; // Not used + STATUS_ROLE_SWITCH_PENDING = 0x32; + STATUS_UNDEFINED_0x33 = 0x33; + STATUS_RESERVED_SLOT_VIOLATION = 0x34; + STATUS_ROLE_SWITCH_FAILED = 0x35; + STATUS_INQ_RSP_DATA_TOO_LARGE = 0x36; + STATUS_SIMPLE_PAIRING_NOT_SUPPORTED = 0x37; + STATUS_HOST_BUSY_PAIRING = 0x38; + STATUS_REJ_NO_SUITABLE_CHANNEL = 0x39; + STATUS_CONTROLLER_BUSY = 0x3A; + STATUS_UNACCEPT_CONN_INTERVAL = 0x3B; + STATUS_ADVERTISING_TIMEOUT = 0x3C; + STATUS_CONN_TOUT_DUE_TO_MIC_FAILURE = 0x3D; + STATUS_CONN_FAILED_ESTABLISHMENT = 0x3E; + STATUS_MAC_CONNECTION_FAILED = 0x3F; + STATUS_LT_ADDR_ALREADY_IN_USE = 0x40; + STATUS_LT_ADDR_NOT_ALLOCATED = 0x41; + STATUS_CLB_NOT_ENABLED = 0x42; + STATUS_CLB_DATA_TOO_BIG = 0x43; + STATUS_OPERATION_CANCELED_BY_HOST = 0x44; // Not currently used in system/bt +} diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/core/proto/android/bluetooth/hfp/enums.proto index c763c175687f..d286e4b64d67 100644 --- a/services/core/java/com/android/server/wm/StackWindowListener.java +++ b/core/proto/android/bluetooth/hfp/enums.proto @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * 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. @@ -11,19 +11,18 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.server.wm; +syntax = "proto2"; +package android.bluetooth.hfp; -import android.graphics.Rect; +option java_outer_classname = "BluetoothHfpProtoEnums"; +option java_multiple_files = true; -/** - * Interface used by the creator of {@link StackWindowController} to listen to changes with - * the stack container. - */ -public interface StackWindowListener extends WindowContainerListener { - - /** Called when the stack container would like its controller to resize. */ - void requestResize(Rect bounds); -} +enum ScoCodec { + SCO_CODEC_UNKNOWN = 0; + SCO_CODEC_CVSD = 1; + // Default codec behind Wide Band Speech + SCO_CODEC_MSBC = 2; +}
\ 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 cc5aa20258ce..415857771899 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -436,12 +436,19 @@ message GlobalSettingsProto { // Ordered GPU debug layer list for GLES // i.e. <layer1>:<layer2>:...:<layerN> optional SettingProto debug_layers_gles = 7; - // GUP - List of Apps selected to use Game Update Packages - optional SettingProto gup_dev_opt_in_apps = 8; - // GUP - List of Apps selected not to use Game Update Packages - optional SettingProto gup_dev_opt_out_apps = 9; - // GUP - List of Apps that are forbidden to use Game Update Packages - optional SettingProto gup_black_list = 10; + // GUP - Game Update Package global preference for all Apps + // 0 = Default + // 1 = All Apps use Game Update Package + // 2 = All Apps use system graphics driver + optional SettingProto gup_dev_all_apps = 8; + // GUP - List of Apps selected to use Game Update Package + // i.e. <pkg1>,<pkg2>,...,<pkgN> + optional SettingProto gup_dev_opt_in_apps = 9; + // GUP - List of Apps selected not to use Game Update Package + // i.e. <pkg1>,<pkg2>,...,<pkgN> + optional SettingProto gup_dev_opt_out_apps = 10; + // GUP - List of Apps that are forbidden to use Game Update Package + optional SettingProto gup_blacklist = 11; } optional Gpu gpu = 59; diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 0e052fe8d4b1..4bfd4d236abb 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -182,6 +182,7 @@ message SecureSettingsProto { optional SettingProto pulse_on_pick_up = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto pulse_on_long_press = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto pulse_on_double_tap = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pulse_on_tap = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Doze doze = 21; @@ -525,7 +526,10 @@ message SecureSettingsProto { } optional Zen zen = 71; + optional SettingProto skip_gesture_enabled = 74 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto silence_gesture_enabled = 75 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 74; + // Next tag = 76; } diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 0ec8c1ada47e..7f3ea7a249ba 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -403,18 +403,23 @@ message StateControllerProto { optional bool is_charging = 1; optional bool is_in_parole = 2; + // List of UIDs currently in the foreground. + repeated int32 foreground_uids = 3; + message TrackedJob { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; optional JobStatusDumpProto.Bucket effective_standby_bucket = 3; - optional bool has_quota = 4; + // If the job started while the app was in the TOP state. + optional bool is_top_started_job = 4; + optional bool has_quota = 5; // The amount of time that this job has remaining in its quota. This // can be negative if the job is out of quota. - optional int64 remaining_quota_ms = 5; + optional int64 remaining_quota_ms = 6; } - repeated TrackedJob tracked_jobs = 3; + repeated TrackedJob tracked_jobs = 4; message Package { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -456,7 +461,7 @@ message StateControllerProto { repeated TimingSession saved_sessions = 3; } - repeated PackageStats package_stats = 4; + repeated PackageStats package_stats = 5; } message StorageController { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 2f3a4910bf83..f3e32414ee26 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1186,6 +1186,7 @@ <!-- Used for permissions that are associated with activity recognition. TODO(zezeozue). STOPSHIP: Add icon --> <permission-group android:name="android.permission-group.ACTIVITY_RECOGNITION" + android:icon="@drawable/perm_group_activity_recognition" android:label="@string/permgrouplab_activityRecognition" android:description="@string/permgroupdesc_activityRecognition" android:request="@string/permgrouprequest_activityRecognition" @@ -2532,6 +2533,16 @@ <permission android:name="android.permission.WRITE_GSERVICES" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows an application to modify config settings. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.WRITE_DEVICE_CONFIG" + android:protectionLevel="signature|configurator"/> + + <!-- @SystemApi @hide Allows an application to read config settings. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.READ_DEVICE_CONFIG" + android:protectionLevel="signature|preinstalled" /> + <!-- @SystemApi @TestApi Allows an application to call {@link android.app.ActivityManager#forceStopPackage}. @hide --> @@ -3196,9 +3207,10 @@ <!-- @SystemApi Required to add or remove another application as a device admin. <p>Not for use by third-party applications. - @hide --> + @hide + @removed --> <permission android:name="android.permission.MANAGE_DEVICE_ADMINS" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature" /> <!-- @SystemApi Allows an app to reset the device password. <p>Not for use by third-party applications. @@ -3520,19 +3532,25 @@ <permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to provide remote displays. + <p>Not for use by third-party applications.</p> + @hide --> + <permission android:name="android.permission.REMOTE_DISPLAY_PROVIDER" + android:protectionLevel="signature|privileged" /> + <!-- Allows an application to capture video output. <p>Not for use by third-party applications.</p> @hide @removed --> <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature" /> <!-- Allows an application to capture secure video output. <p>Not for use by third-party applications.</p> @hide @removed --> <permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature" /> <!-- Allows an application to know what content is playing and control its playback. <p>Not for use by third-party applications due to privacy of media consumption</p> --> diff --git a/core/res/res/drawable/perm_group_activity_recognition.xml b/core/res/res/drawable/perm_group_activity_recognition.xml new file mode 100644 index 000000000000..0ade6c674171 --- /dev/null +++ b/core/res/res/drawable/perm_group_activity_recognition.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#000000" + android:pathData="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 +2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 +0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5 .1 -.8 .1 l-5.2 +2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" /> +</vector> diff --git a/core/res/res/drawable/perm_group_sensors.xml b/core/res/res/drawable/perm_group_sensors.xml index ce36c13e1690..e4663d7206fc 100644 --- a/core/res/res/drawable/perm_group_sensors.xml +++ b/core/res/res/drawable/perm_group_sensors.xml @@ -19,11 +19,11 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - - <path - android:fillColor="#000000" - android:pathData="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 -2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 -0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5 .1 -.8 .1 l-5.2 -2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" /> -</vector>
\ No newline at end of file + <path + android:fillColor="#000000" + android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3C4.42,3 2,5.42 2, + 8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5C22, + 5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1l-0.1,-0.1C7.14,14.24 4,11.39 4,8.5C4,6.5 5.5, + 5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5C20, + 11.39 16.86,14.24 12.1,18.55z"/> +</vector> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 18a42bc5ed0b..b15f72efd717 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -274,6 +274,9 @@ <!-- Additional flag from base permission type: this permission can be automatically granted to the document manager --> <flag name="documenter" value="0x40000" /> + <!-- Additional flag from base permission type: this permission automatically + granted to device configurator --> + <flag name="configurator" value="0x80000" /> </attr> <!-- Flags indicating more context for a permission group. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d40f01c7c960..64fdc026cc68 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -618,15 +618,6 @@ <!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds --> <integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer> - <!-- Integer indicating the framework scan interval in milliseconds. This is used in the scenario - where the chipset does not support background scanning (config_wifi_background_scan_suport - is false) to set up a periodic wake up scan so that the device can connect to a new access - point on the move. A value of 0 means no periodic scans will be used in the framework. --> - <integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer> - - <!-- Integer indicating the framework no networks periodic scan interval in milliseconds. --> - <integer translatable="false" name="config_wifi_no_network_periodic_scan_interval">300000</integer> - <!-- Integer indicating disconnect mode short scan interval in milliseconds --> <integer translatable="false" name="config_wifi_disconnected_short_scan_interval">15000</integer> @@ -692,6 +683,11 @@ <!-- Wifi driver supports Automatic channel selection (ACS) for softap --> <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool> + <!-- Channel list restriction to Automatic channel selection (ACS) for softap. If the device + doesn't want to restrict channels this should be empty. Value is a comma separated channel + string and/or channel range string like '1-6,11' --> + <string translatable="false" name="config_wifi_softap_acs_supported_channel_list"></string> + <!-- Wifi driver supports IEEE80211AC for softap --> <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool> @@ -699,6 +695,9 @@ for automotive builds only (the one that have PackageManager#FEATURE_AUTOMOTIVE) --> <bool translatable="false" name="config_wifi_local_only_hotspot_5ghz">false</bool> + <!-- Indicates that connected MAC randomization is supported on this device --> + <bool translatable="false" name="config_wifi_connected_mac_randomization_supported">false</bool> + <!-- Flag indicating whether we should enable the automatic brightness. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> @@ -1689,6 +1688,8 @@ config_enableFusedLocationOverlay is false. --> <string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string> + <string-array name="config_locationExtraPackageNames" translatable="false"></string-array> + <!-- The package name of the default network recommendation app. A network recommendation provider must: * Be granted the SCORE_NETWORKS permission. @@ -1890,6 +1891,8 @@ 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. @@ -1908,6 +1911,12 @@ the behavior will be as though no app was named as an explicit default. --> <string name="default_browser" translatable="false"></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> + </string-array> + <!-- Enable/disable default bluetooth profiles: HSP_AG, ObexObjectPush, Audio, NAP --> <bool name="config_bluetooth_default_profiles">true</bool> @@ -2203,6 +2212,9 @@ <!-- Type of the double tap sensor. Empty if double tap is not supported. --> <string name="config_dozeDoubleTapSensorType" translatable="false"></string> + <!-- Type of the tap sensor. Empty if tap is not supported. --> + <string name="config_dozeTapSensorType" translatable="false"></string> + <!-- Type of the long press sensor. Empty if long press is not supported. --> <string name="config_dozeLongPressSensorType" translatable="false"></string> @@ -2800,6 +2812,9 @@ <!-- Flag indicating which package name can access the persistent data partition --> <string name="config_persistentDataPackageName" translatable="false"></string> + <!-- Flag indicating which package name can access DeviceConfig table --> + <string name="config_deviceConfiguratorPackageName" translatable="false"></string> + <!-- Flag indicating apps will skip sending hold request before merge. In this case IMS service implementation will do both.i.e.hold followed by merge. --> <bool name="skipHoldBeforeMerge">true</bool> @@ -3696,7 +3711,7 @@ <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string> <!-- Component name for the default module metadata provider on this device --> - <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string> + <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string> <!-- This is the default launcher component to use on secondary displays that support system decorations. @@ -3708,4 +3723,10 @@ <!-- If device supports corner radius on windows. This should be turned off on low-end devices to improve animation performance. --> <bool name="config_supportsRoundedCornersOnWindows">true</bool> + + <!-- If the sensor that skips media is available or not. --> + <bool name="config_skipSensorAvailable">false</bool> + + <!-- If the sensor that silences alerts is available or not. --> + <bool name="config_silenceSensorAvailable">false</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index b3b30e992302..777886a9911c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2985,6 +2985,11 @@ <public name="system_notification_accent_color" /> </public-group> + <public-group type="array" first-id="0x01070006"> + <!-- @hide @SystemApi --> + <public name="config_defaultRoleHolders" /> + </public-group> + <!-- =============================================================== DO NOT ADD UN-GROUPED ITEMS HERE diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a1ef0d5cc93b..9317cff054ad 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -325,6 +325,7 @@ <java-symbol type="bool" name="config_forceDefaultOrientation" /> <java-symbol type="bool" name="config_wifi_batched_scan_supported" /> <java-symbol type="bool" name="config_wifi_softap_acs_supported" /> + <java-symbol type="string" name="config_wifi_softap_acs_supported_channel_list" /> <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" /> <java-symbol type="bool" name="config_enableMultiUserUI"/> <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/> @@ -458,9 +459,7 @@ <java-symbol type="integer" name="config_toastDefaultGravity" /> <java-symbol type="integer" name="config_triplePressOnPowerBehavior" /> <java-symbol type="integer" name="config_shortPressOnSleepBehavior" /> - <java-symbol type="integer" name="config_wifi_framework_scan_interval" /> <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" /> - <java-symbol type="integer" name="config_wifi_no_network_periodic_scan_interval" /> <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" /> <java-symbol type="integer" name="config_windowOutsetBottom" /> <java-symbol type="integer" name="db_connection_pool_size" /> @@ -1840,6 +1839,7 @@ <java-symbol type="array" name="radioAttributes" /> <java-symbol type="array" name="config_oemUsbModeOverride" /> <java-symbol type="array" name="config_locationProviderPackageNames" /> + <java-symbol type="array" name="config_locationExtraPackageNames" /> <java-symbol type="array" name="config_testLocationProviders" /> <java-symbol type="array" name="config_defaultNotificationVibePattern" /> <java-symbol type="array" name="config_notificationFallbackVibePattern" /> @@ -1879,6 +1879,7 @@ <java-symbol type="bool" name="config_wifi_dual_band_support" /> <java-symbol type="bool" name="config_wifi_convert_apband_5ghz_to_any" /> <java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" /> + <java-symbol type="bool" name="config_wifi_connected_mac_randomization_supported" /> <java-symbol type="bool" name="config_wifi_fast_bss_transition_enabled" /> <java-symbol type="bool" name="config_wimaxEnabled" /> <java-symbol type="bool" name="show_ongoing_ime_switcher" /> @@ -2139,6 +2140,7 @@ <java-symbol type="string" name="config_carrierAppInstallDialogComponent" /> <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> <java-symbol type="string" name="config_persistentDataPackageName" /> + <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> <java-symbol type="layout" name="resolver_list" /> <java-symbol type="id" name="resolver_list" /> @@ -3001,6 +3003,7 @@ <java-symbol type="array" name="config_emergency_mcc_codes" /> <java-symbol type="string" name="config_dozeDoubleTapSensorType" /> + <java-symbol type="string" name="config_dozeTapSensorType" /> <java-symbol type="bool" name="config_dozePulsePickup" /> <!-- Used for MimeIconUtils. --> @@ -3536,4 +3539,7 @@ <java-symbol type="string" name="dynamic_mode_notification_title" /> <java-symbol type="string" name="dynamic_mode_notification_summary" /> <java-symbol type="drawable" name="ic_battery" /> + + <java-symbol type="bool" name="config_skipSensorAvailable" /> + <java-symbol type="bool" name="config_silenceSensorAvailable" /> </resources> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 74943c7bd6dd..0fc3bd224fbf 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -36,12 +36,12 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ frameworks-core-util-lib \ mockwebserver \ guava \ - android-support-test \ + androidx.test.runner \ + androidx.test.rules \ mockito-target-minus-junit4 \ espresso-core \ ub-uiautomator \ platform-test-annotations \ - compatibility-device-util \ truth-prebuilt \ print-test-util-lib \ testng # TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 46d4a4773389..86818c611c1f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -56,6 +56,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> <uses-permission android:name="android.permission.WRITE_DREAM_STATE" /> <uses-permission android:name="android.permission.READ_LOGS"/> @@ -65,6 +66,7 @@ <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> @@ -1425,7 +1427,7 @@ </application> - <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.frameworks.coretests" android:label="Frameworks Core Tests" /> <key-sets> diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 68ef34b279e6..b40aa87cb78b 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -25,6 +25,7 @@ <option name="test-tag" value="FrameworksCoreTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.frameworks.coretests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> </configuration> diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java index e26bdf53b872..be1d44cec6a6 100644 --- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase2; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import androidx.test.filters.LargeTest; import com.android.frameworks.coretests.R; +import java.util.HashSet; +import java.util.Set; + @LargeTest public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { Set<Integer> identityHashes = new HashSet<Integer>(); diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivity.java b/core/tests/coretests/src/android/animation/AnimatorSetActivity.java index 501ea485b8b2..af265afa83e5 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivity.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivity.java @@ -1,10 +1,26 @@ -package android.animation; +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.android.frameworks.coretests.R; +package android.animation; import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + public class AnimatorSetActivity extends Activity { @Override public void onCreate(Bundle savedBundleInstance) { diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index 922bc59d71d1..55837ba3926c 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -1,12 +1,29 @@ -package android.animation; +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.android.frameworks.coretests.R; +package android.animation; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.SmallTest; import android.view.View; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; + +import com.android.frameworks.coretests.R; + import java.util.ArrayList; public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> { diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java index 7eb32ee36876..4e90d1acf4a0 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; import android.os.Handler; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; import android.widget.Button; + +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; import java.util.concurrent.TimeUnit; diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java index b1f88db86473..b3ec92cf1f0f 100644 --- a/core/tests/coretests/src/android/animation/AutoCancelTest.java +++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; import android.os.Handler; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import java.util.HashMap; import java.util.concurrent.TimeUnit; diff --git a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java index 0e1e6ac98672..2b9866d7a0b2 100644 --- a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java +++ b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.animation; -import com.android.frameworks.coretests.R; +package android.animation; import android.app.Activity; import android.os.Bundle; import android.widget.Button; +import com.android.frameworks.coretests.R; + public class BasicAnimatorActivity extends Activity { public Button mAnimatingButton; @Override diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java index 28cfe3d5d68b..ba7413a2a4f7 100644 --- a/core/tests/coretests/src/android/animation/EventsTest.java +++ b/core/tests/coretests/src/android/animation/EventsTest.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; import android.os.Handler; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java index 0c65e20be6a5..0c09a4a27d93 100644 --- a/core/tests/coretests/src/android/animation/FutureWaiter.java +++ b/core/tests/coretests/src/android/animation/FutureWaiter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; import com.google.common.util.concurrent.AbstractFuture; diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java index 606a93949a54..53f94727f6d3 100644 --- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java +++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; import android.widget.Button; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java index a9961e19e0c6..e755b89f8551 100644 --- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java +++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java @@ -14,16 +14,16 @@ * limitations under the License. */ - package android.animation; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; import android.util.StateSet; import android.view.View; import android.view.ViewGroup; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.LargeTest; + import com.android.frameworks.coretests.R; import java.util.concurrent.atomic.AtomicInteger; diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java index c25d050dd3f4..f6d71b88bd89 100644 --- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; /** diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java index 4facf7742153..dee0a3ecdbe0 100644 --- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; +import static android.test.MoreAsserts.assertNotEqual; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -24,23 +27,20 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.view.Choreographer; import android.view.animation.LinearInterpolator; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; -import static android.test.MoreAsserts.assertNotEqual; - @RunWith(AndroidJUnit4.class) @MediumTest public class ValueAnimatorTests { diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java index 30ec182f757c..997af009a9b8 100644 --- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java +++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java @@ -13,15 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.animation; import android.os.Handler; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; import android.view.ViewPropertyAnimator; import android.widget.Button; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; import java.util.concurrent.TimeUnit; diff --git a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java index 19a390ab1a11..8dc5ad66194b 100644 --- a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java +++ b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java @@ -16,15 +16,16 @@ package android.app; -import org.junit.Test; -import org.junit.runner.RunWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.app.ApplicationErrorReport.CrashInfo; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java index 063bef7387c6..4b0ed65e5fde 100644 --- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java +++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java @@ -16,13 +16,17 @@ package android.app; +import static android.os.storage.VolumeInfo.STATE_MOUNTED; +import static android.os.storage.VolumeInfo.STATE_UNMOUNTED; + import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import junit.framework.TestCase; @@ -32,9 +36,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static android.os.storage.VolumeInfo.STATE_MOUNTED; -import static android.os.storage.VolumeInfo.STATE_UNMOUNTED; - @LargeTest public class ApplicationPackageManagerTest extends TestCase { private static final String sInternalVolPath = "/data"; diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java index c1d4be0f33c3..33e040253f06 100644 --- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java @@ -22,7 +22,8 @@ import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; -import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.filters.LargeTest; import com.google.mockwebserver.MockResponse; diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java index 39d9a8e31ddc..adfe76f2f151 100644 --- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java @@ -23,10 +23,11 @@ import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.StatFs; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.Log; +import androidx.test.filters.LargeTest; +import androidx.test.filters.Suppress; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; diff --git a/core/tests/coretests/src/android/app/InstrumentationTest.java b/core/tests/coretests/src/android/app/InstrumentationTest.java index 9b59da48e760..93b5aecfc8f7 100644 --- a/core/tests/coretests/src/android/app/InstrumentationTest.java +++ b/core/tests/coretests/src/android/app/InstrumentationTest.java @@ -17,9 +17,10 @@ package android.app; import android.os.Bundle; -import android.support.test.filters.LargeTest; import android.test.InstrumentationTestCase; +import androidx.test.filters.LargeTest; + @LargeTest public class InstrumentationTest extends InstrumentationTestCase { diff --git a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java index c83e7988dcc6..e343383df3f1 100644 --- a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java +++ b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package android.app; import static junit.framework.TestCase.assertNotNull; @@ -27,11 +26,12 @@ import static org.junit.Assert.fail; import android.content.Context; import android.os.Handler; import android.os.Parcelable; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index e89a4d3bf94f..c17aa9254808 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -33,11 +33,12 @@ import android.media.session.MediaSession; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.widget.RemoteViews; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java index 08b7f60c368f..14370c887675 100644 --- a/core/tests/coretests/src/android/app/SearchManagerTest.java +++ b/core/tests/coretests/src/android/app/SearchManagerTest.java @@ -17,17 +17,13 @@ package android.app; import android.app.activity.LocalActivity; - -import android.app.Activity; -import android.app.ISearchManager; -import android.app.SearchManager; -import android.app.SearchableInfo; import android.content.ComponentName; import android.content.Context; import android.os.ServiceManager; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; /** * To launch this test from the command line: diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java index 61d73bc918d5..bbd442d53f76 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java @@ -21,8 +21,9 @@ import android.content.Context; import android.content.pm.ConfigurationInfo; import android.content.res.Configuration; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; import java.util.Iterator; import java.util.List; diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 0f83a29d8d36..9cb34895dea4 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -36,13 +36,14 @@ import android.app.servertransaction.StopActivityItem; import android.content.Intent; import android.content.res.Configuration; import android.os.IBinder; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.util.MergedConfiguration; import android.view.Display; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java index 13e70ebdb2e3..0f81896692c0 100644 --- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java +++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java @@ -27,11 +27,10 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.UserHandle; -import android.support.test.filters.LargeTest; -import android.test.FlakyTest; import android.util.Log; -import java.util.Arrays; +import androidx.test.filters.FlakyTest; +import androidx.test.filters.LargeTest; @LargeTest public class BroadcastTest extends ActivityTestsBase { @@ -231,7 +230,7 @@ public class BroadcastTest extends ActivityTestsBase { }; // Mark flaky until http://b/issue?id=1191607 is resolved. - @FlakyTest(tolerance=2) + @FlakyTest public void testRegistered() throws Exception { runLaunchpad(LaunchpadActivity.BROADCAST_REGISTERED); } @@ -248,12 +247,12 @@ public class BroadcastTest extends ActivityTestsBase { runLaunchpad(LaunchpadActivity.BROADCAST_ABORT); } - @FlakyTest(tolerance=2) + @FlakyTest public void testAll() throws Exception { runLaunchpad(LaunchpadActivity.BROADCAST_ALL); } - @FlakyTest(tolerance=2) + @FlakyTest public void testMulti() throws Exception { runLaunchpad(LaunchpadActivity.BROADCAST_MULTI); } @@ -348,7 +347,7 @@ public class BroadcastTest extends ActivityTestsBase { } // Marking flaky until http://b/issue?id=1191337 is resolved - @FlakyTest(tolerance=2) + @FlakyTest public void testReceiveSticky() throws Exception { Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null); intent.putExtra("test", LaunchpadActivity.DATA_1); @@ -358,7 +357,7 @@ public class BroadcastTest extends ActivityTestsBase { } // Marking flaky until http://b/issue?id=1191337 is resolved - @FlakyTest(tolerance=2) + @FlakyTest public void testReceive2Sticky() throws Exception { Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null); intent.putExtra("test", LaunchpadActivity.DATA_1); diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java index 8c1d79b5d89c..19ddb52db86e 100644 --- a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java +++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java @@ -21,7 +21,8 @@ import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; @LargeTest public class IntentSenderTest extends BroadcastTest { diff --git a/core/tests/coretests/src/android/app/activity/LaunchTest.java b/core/tests/coretests/src/android/app/activity/LaunchTest.java index 5b86dceb75f8..6846ea75e524 100644 --- a/core/tests/coretests/src/android/app/activity/LaunchTest.java +++ b/core/tests/coretests/src/android/app/activity/LaunchTest.java @@ -17,8 +17,9 @@ package android.app.activity; import android.content.ComponentName; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.Suppress; @Suppress // Flaky. public class LaunchTest extends ActivityTestsBase { diff --git a/core/tests/coretests/src/android/app/activity/LifecycleTest.java b/core/tests/coretests/src/android/app/activity/LifecycleTest.java index ed01fac5e0a9..5aa03807264e 100644 --- a/core/tests/coretests/src/android/app/activity/LifecycleTest.java +++ b/core/tests/coretests/src/android/app/activity/LifecycleTest.java @@ -18,8 +18,9 @@ package android.app.activity; import android.content.ComponentName; import android.content.Intent; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; public class LifecycleTest extends ActivityTestsBase { private Intent mTopIntent; diff --git a/core/tests/coretests/src/android/app/activity/MetaDataTest.java b/core/tests/coretests/src/android/app/activity/MetaDataTest.java index 5b9c0e903135..cf27878e897e 100644 --- a/core/tests/coretests/src/android/app/activity/MetaDataTest.java +++ b/core/tests/coretests/src/android/app/activity/MetaDataTest.java @@ -27,8 +27,11 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java index 9f402a5265ec..8184627494ed 100644 --- a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java +++ b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java @@ -1,19 +1,18 @@ -/* //device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java -** -** Copyright 2006, 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 (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package android.app.activity; @@ -21,7 +20,8 @@ import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Process; -import android.util.Log; + +//device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java public class RemoteSubActivityScreen extends SubActivityScreen { Handler mHandler = new Handler(); diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java index d3ae415223e0..9d2aebd1e6cd 100644 --- a/core/tests/coretests/src/android/app/activity/ServiceTest.java +++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java @@ -22,13 +22,12 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; -import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; -import android.util.Log; +import android.os.RemoteException; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; // These test binders purport to support an interface whose canonical // interface name is ServiceTest.SERVICE_LOCAL diff --git a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java index 41b95476baf3..8e172952c9ee 100644 --- a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java +++ b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java @@ -19,7 +19,8 @@ package android.app.activity; import android.app.AlarmManager; import android.content.Context; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.filters.LargeTest; import java.util.TimeZone; diff --git a/core/tests/coretests/src/android/app/activity/SubActivityTest.java b/core/tests/coretests/src/android/app/activity/SubActivityTest.java index 35dde8a51567..53f89fe1276b 100644 --- a/core/tests/coretests/src/android/app/activity/SubActivityTest.java +++ b/core/tests/coretests/src/android/app/activity/SubActivityTest.java @@ -16,9 +16,10 @@ package android.app.activity; -import android.test.suitebuilder.annotation.Suppress; import android.content.ComponentName; +import androidx.test.filters.Suppress; + @Suppress public class SubActivityTest extends ActivityTestsBase { diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index 9b5b725a3bed..8d42c74be7b0 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -27,8 +27,9 @@ import static org.junit.Assert.assertNotEquals; import android.app.admin.PasswordMetrics.PasswordComplexityBucket; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java index 689e683dda23..1f4e5dffc84e 100644 --- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java +++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.assist; import static android.view.View.AUTOFILL_TYPE_TEXT; @@ -22,13 +23,9 @@ import static android.view.View.IMPORTANT_FOR_AUTOFILL_YES; import static com.google.common.truth.Truth.assertThat; import android.app.assist.AssistStructure.ViewNode; -import android.content.ComponentName; import android.content.Context; import android.os.Parcel; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.text.InputFilter; import android.util.Log; import android.view.autofill.AutofillId; @@ -37,6 +34,10 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.test.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java index f4b6bed1cac8..defec433e403 100644 --- a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java +++ b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.assist; import android.app.Activity; diff --git a/core/tests/coretests/src/android/app/backup/BackupDataTest.java b/core/tests/coretests/src/android/app/backup/BackupDataTest.java index 5b8e481cf573..18ff54fa6989 100644 --- a/core/tests/coretests/src/android/app/backup/BackupDataTest.java +++ b/core/tests/coretests/src/android/app/backup/BackupDataTest.java @@ -16,31 +16,22 @@ package android.app.backup; -import android.app.backup.BackupDataInput; -import android.app.backup.BackupDataOutput; -import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; -import android.test.InstrumentationTestCase; import android.util.Base64; -import android.util.Log; -import org.json.JSONObject; + +import androidx.test.filters.LargeTest; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.Exception; -import java.nio.ByteBuffer; @LargeTest public class BackupDataTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java index 5db416b33af7..08edb4e9be97 100644 --- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java +++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java @@ -18,11 +18,12 @@ package android.app.backup; import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; import android.util.ArrayMap; import android.util.ArraySet; +import androidx.test.filters.LargeTest; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java index b1f855246320..52b26589279f 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java @@ -23,8 +23,9 @@ import static org.mockito.Mockito.verify; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index fb0f5344f643..ad28d13dc46a 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -35,8 +35,9 @@ import android.os.Binder; import android.os.Bundle; import android.os.PersistableBundle; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index a788a938357f..f730a244a593 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -45,10 +45,11 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 2801f32430ef..8604b0c48476 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -50,8 +50,9 @@ import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IVoiceInteractor; diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java index e20645c7d3c8..0efc0ab89a73 100644 --- a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java +++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java @@ -21,7 +21,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Parcel; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import org.junit.Test; diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java index b69054cebbd2..b519bf8b13f7 100644 --- a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java +++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java @@ -21,7 +21,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Parcel; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import org.junit.Test; diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java index dd462403ed82..bb535b6fac7d 100644 --- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java +++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java @@ -16,14 +16,13 @@ package android.app.timezone; -import static junit.framework.Assert.fail; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Parcel; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import org.junit.Test; diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java index 4004086354ca..df9ddea53c91 100644 --- a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java +++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java @@ -25,7 +25,8 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat; import android.content.Context; import android.content.Intent; import android.os.UserHandle; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; diff --git a/core/tests/coretests/src/android/app/usage/EventListTest.java b/core/tests/coretests/src/android/app/usage/EventListTest.java index 9dc0d4309a8f..685fcae46553 100644 --- a/core/tests/coretests/src/android/app/usage/EventListTest.java +++ b/core/tests/coretests/src/android/app/usage/EventListTest.java @@ -20,10 +20,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java index 28aaf1e05644..1633e1ac50a0 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java @@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; @@ -33,8 +34,9 @@ import static org.junit.Assert.fail; import android.app.usage.UsageEvents.Event; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; @@ -528,6 +530,11 @@ public class UsageStatsTest { } @Test + public void testEvent_DEVICE_SHUTDOWN() { + testClosingEvent(DEVICE_SHUTDOWN); + } + + @Test public void testEvent_FLUSH_TO_DISK() { testClosingEvent(FLUSH_TO_DISK); } @@ -535,8 +542,9 @@ public class UsageStatsTest { private void testClosingEvent(int eventType) { // When these three closing events are received, all open activities/services need to be // closed and usage stats are updated. - if (eventType != FLUSH_TO_DISK) { - fail("Closing eventType must be one of FLUSH_TO_DISK"); + if (eventType != DEVICE_SHUTDOWN + && eventType != FLUSH_TO_DISK) { + fail("Closing eventType must be one of DEVICE_SHUTDOWN, FLUSH_TO_DISK"); } left.mPackageName = "com.test"; diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java index 978ea7aa9c00..c307e648752d 100644 --- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java @@ -16,7 +16,6 @@ package android.content; - import static org.junit.Assert.fail; import android.app.ActivityManager; @@ -30,9 +29,10 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/content/AssetTest.java b/core/tests/coretests/src/android/content/AssetTest.java index b66574cee231..8e55e8a1a632 100644 --- a/core/tests/coretests/src/android/content/AssetTest.java +++ b/core/tests/coretests/src/android/content/AssetTest.java @@ -18,7 +18,8 @@ package android.content; import android.content.res.AssetManager; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import java.io.IOException; import java.io.InputStream; diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java index 3d246b457ed5..d8c9baa8df5b 100644 --- a/core/tests/coretests/src/android/content/BrickDeniedTest.java +++ b/core/tests/coretests/src/android/content/BrickDeniedTest.java @@ -17,7 +17,8 @@ package android.content; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; /** Test to make sure brick intents <b>don't</b> work without permission. */ public class BrickDeniedTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java index 8deccb7ffa7f..1509ff960ece 100644 --- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java +++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java @@ -18,9 +18,9 @@ package android.content; import static org.junit.Assert.fail; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/content/ContentProviderOperationTest.java b/core/tests/coretests/src/android/content/ContentProviderOperationTest.java index aea124b08658..b142761760c4 100644 --- a/core/tests/coretests/src/android/content/ContentProviderOperationTest.java +++ b/core/tests/coretests/src/android/content/ContentProviderOperationTest.java @@ -16,22 +16,23 @@ package android.content; -import android.content.ContentValues; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; + +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; -import java.util.Set; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; @SmallTest public class ContentProviderOperationTest extends TestCase { diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java index 2142f27ff6d8..8895f9bdf23d 100644 --- a/core/tests/coretests/src/android/content/ContentProviderTest.java +++ b/core/tests/coretests/src/android/content/ContentProviderTest.java @@ -23,7 +23,8 @@ import static org.mockito.Mockito.withSettings; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; import android.net.Uri; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/content/ContentQueryMapTest.java b/core/tests/coretests/src/android/content/ContentQueryMapTest.java index f47bfdbb28fc..710623488552 100644 --- a/core/tests/coretests/src/android/content/ContentQueryMapTest.java +++ b/core/tests/coretests/src/android/content/ContentQueryMapTest.java @@ -16,16 +16,14 @@ package android.content; -import android.content.ContentQueryMap; -import android.content.ContentResolver; -import android.content.ContentValues; import android.database.Cursor; import android.os.Handler; import android.os.Looper; import android.provider.Settings; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; import java.util.Observable; import java.util.Observer; diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 9940bf7dd692..f14f2896d8f5 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -31,10 +31,11 @@ import android.graphics.Paint; import android.net.Uri; import android.os.MemoryFile; import android.os.ParcelFileDescriptor; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; import android.util.Size; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/content/ContentValuesTest.java b/core/tests/coretests/src/android/content/ContentValuesTest.java index 7b39939b2d0c..0ab79e730ea1 100644 --- a/core/tests/coretests/src/android/content/ContentValuesTest.java +++ b/core/tests/coretests/src/android/content/ContentValuesTest.java @@ -17,8 +17,8 @@ package android.content; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; /* runtest -c android.content.ContentValuesTest frameworks-core @@ -29,7 +29,7 @@ import android.test.suitebuilder.annotation.SmallTest; adb shell pm uninstall -k com.android.frameworks.coretests && \ adb install out/target/product/bullhead/testcases/FrameworksCoreTests/FrameworksCoreTests.apk && \ adb shell am instrument -w -e package android.content \ - com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ public class ContentValuesTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index c8a3098690be..2f442c34f2c1 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -19,11 +19,12 @@ package android.content; import static org.junit.Assert.assertEquals; import android.app.ActivityThread; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.WindowManager; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java index 4362ec3a0ed6..22b23148cbcf 100644 --- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java @@ -19,7 +19,8 @@ package android.content; import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; /** * To run the tests, use @@ -32,7 +33,7 @@ import android.support.test.filters.LargeTest; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ @LargeTest public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest { diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java index bbe7c1008299..7cd486222101 100644 --- a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java +++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java @@ -18,9 +18,10 @@ package android.content; import android.net.Uri; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; import java.io.InputStream; import java.util.Arrays; diff --git a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java index 10d74f7d2945..fd5de3217389 100644 --- a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java +++ b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License */ + package android.content; import android.os.Bundle; import android.os.Parcelable; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; +import androidx.test.filters.LargeTest; + import java.util.Arrays; import java.util.HashSet; import java.util.List; diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java index f8b13f032652..dbe027800e3f 100644 --- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java @@ -18,7 +18,8 @@ package android.content; import android.content.pm.UserInfo; import android.os.RemoteException; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; /** * To run the tests, use @@ -31,7 +32,7 @@ import android.support.test.filters.LargeTest; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ @LargeTest public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest { diff --git a/core/tests/coretests/src/android/content/UriMatcherTest.java b/core/tests/coretests/src/android/content/UriMatcherTest.java index f3b9e76f3bcb..6cef46b0731a 100644 --- a/core/tests/coretests/src/android/content/UriMatcherTest.java +++ b/core/tests/coretests/src/android/content/UriMatcherTest.java @@ -17,14 +17,14 @@ package android.content; import android.net.Uri; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; import java.lang.reflect.Field; import java.util.ArrayList; - public class UriMatcherTest extends TestCase { static final int ROOT = 0; diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java index 2acb08da4ef0..9b360db023d0 100644 --- a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java +++ b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static android.content.pm.PackageBuilder.builder; @@ -20,7 +21,8 @@ import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE; import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER; import android.os.Build; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java index dce22ce6a6f2..0ed76dcf7c5d 100644 --- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java +++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static android.content.pm.PackageBuilder.builder; import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE; import android.os.Build; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java index 866de93c07fe..7f817d66caf7 100644 --- a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java +++ b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static android.content.pm.PackageBuilder.builder; @@ -20,7 +21,8 @@ import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK; import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER; import android.content.pm.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/ComponentTest.java b/core/tests/coretests/src/android/content/pm/ComponentTest.java index cc7564165bba..f31f0b5ee1b4 100644 --- a/core/tests/coretests/src/android/content/pm/ComponentTest.java +++ b/core/tests/coretests/src/android/content/pm/ComponentTest.java @@ -21,7 +21,14 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.GET_DISABLED_COMPONENTS; -import android.test.suitebuilder.annotation.Suppress; +import android.content.ComponentName; +import android.content.Intent; +import android.test.AndroidTestCase; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; + import com.android.frameworks.coretests.enabled_app.DisabledActivity; import com.android.frameworks.coretests.enabled_app.DisabledProvider; import com.android.frameworks.coretests.enabled_app.DisabledReceiver; @@ -31,12 +38,6 @@ import com.android.frameworks.coretests.enabled_app.EnabledProvider; import com.android.frameworks.coretests.enabled_app.EnabledReceiver; import com.android.frameworks.coretests.enabled_app.EnabledService; -import android.content.ComponentName; -import android.content.Intent; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; - import java.util.List; /** diff --git a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java index 1f762fdc49f4..1c703ab0b98f 100644 --- a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java +++ b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java @@ -17,7 +17,8 @@ package android.content.pm; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.MediumTest; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java index 659f9ea77956..1ddd753de80e 100644 --- a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java +++ b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java @@ -16,9 +16,12 @@ package android.content.pm; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; +import androidx.test.filters.LargeTest; + +import libcore.io.Streams; + import java.io.ByteArrayInputStream; import java.util.Arrays; @@ -26,8 +29,6 @@ import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import libcore.io.Streams; - @LargeTest public class MacAuthenticatedInputStreamTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java b/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java index 91697c0d74f3..05db8ee29844 100644 --- a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java +++ b/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import org.junit.Assume; diff --git a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java index dcd2707a9540..834a0bbeab89 100644 --- a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java +++ b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static android.content.pm.PackageBuilder.builder; import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import android.os.Build; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java index c64d5202e1fd..3d7aab001227 100644 --- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java @@ -24,7 +24,8 @@ import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary; import android.os.Build; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Assume; import org.junit.Test; diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java index 4ceed834aab7..c5db9622b60d 100644 --- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java +++ b/core/tests/coretests/src/android/content/pm/PackageBuilder.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static org.junit.Assert.assertEquals; diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 8ac9451deaf6..0ab536779296 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -54,11 +54,12 @@ import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.Log; +import androidx.test.filters.LargeTest; +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; + import com.android.frameworks.coretests.R; import com.android.internal.content.PackageHelper; diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java index 00be82219e4f..e852f989a17f 100644 --- a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java @@ -22,8 +22,9 @@ import android.content.pm.PackageParserCacheHelper.ReadHelper; import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.os.Bundle; import android.os.Parcel; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java index 267267ed4777..be1b1ce48276 100644 --- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java @@ -29,9 +29,10 @@ import android.os.Build; import android.os.Bundle; import android.os.FileUtils; import android.os.SystemProperties; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; @@ -51,6 +52,12 @@ public class PackageParserTest { private static final String PRE_RELEASE = "B"; private static final String NEWER_PRE_RELEASE = "C"; + // Codenames with a fingerprint attached to them. These may only be present in the apps + // declared min SDK and not as platform codenames. + private static final String OLDER_PRE_RELEASE_WITH_FINGERPRINT = "A.fingerprint"; + private static final String PRE_RELEASE_WITH_FINGERPRINT = "B.fingerprint"; + private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "C.fingerprint"; + private static final String[] CODENAMES_RELEASED = { /* empty */ }; private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE }; @@ -68,7 +75,7 @@ public class PackageParserTest { isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE, outError); - assertEquals(result, expectedMinSdk); + assertEquals("Error msg: " + outError[0], expectedMinSdk, result); if (expectedMinSdk == -1) { assertNotNull(outError[0]); @@ -98,6 +105,7 @@ public class PackageParserTest { // APP: Pre-release API 10 // DEV: Pre-release API 20 verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1); + verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1); // Do allow same pre-release minSdkVersion on pre-release platform, // but overwrite the specified version with CUR_DEVELOPMENT. @@ -105,11 +113,15 @@ public class PackageParserTest { // DEV: Pre-release API 20 verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false, Build.VERSION_CODES.CUR_DEVELOPMENT); + verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false, + Build.VERSION_CODES.CUR_DEVELOPMENT); + // Don't allow newer pre-release minSdkVersion on pre-release platform. // APP: Pre-release API 30 // DEV: Pre-release API 20 verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1); + verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1); } @Test @@ -133,16 +145,20 @@ public class PackageParserTest { // APP: Pre-release API 10 // DEV: Released API 20 verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1); + verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1); // Don't allow same pre-release minSdkVersion on released platform. // APP: Pre-release API 20 // DEV: Released API 20 verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1); + verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1); + // Don't allow newer pre-release minSdkVersion on released platform. // APP: Pre-release API 30 // DEV: Released API 20 verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1); + verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1); } private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename, @@ -189,6 +205,9 @@ public class PackageParserTest { // 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 */); + // Do allow same pre-release targetSdkVersion on pre-release platform, // but overwrite the specified version with CUR_DEVELOPMENT. @@ -196,18 +215,26 @@ public class PackageParserTest { // DEV: Pre-release API 20 verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false, Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */); + verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false, + Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */); + // 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 */); } @Test @@ -235,18 +262,25 @@ public class PackageParserTest { // 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 */); // 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 */); + // 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 */); } /** diff --git a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java index d5d3d7a6f2f4..71a0e5e51b71 100644 --- a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import java.util.function.Supplier; diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java index 952bb5530c15..8874525ebc77 100644 --- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java +++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java @@ -1,8 +1,25 @@ +/* + * Copyright (C) 2014 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.os.Parcel; import android.os.Parcelable; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index d3d1f22af3cb..365e97ded928 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -21,11 +21,12 @@ import android.os.FileUtils; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; import android.util.AttributeSet; import android.util.SparseArray; +import androidx.test.filters.LargeTest; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java index 3dba4406de47..216b0c8950b7 100644 --- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java +++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static android.content.pm.PackageBuilder.builder; @@ -20,7 +21,8 @@ import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE; import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary; import android.os.Build; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java index 15b27d71f81d..fc60980bb796 100644 --- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java +++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.pm; import static android.content.pm.PackageBuilder.builder; @@ -20,7 +21,8 @@ import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary; import android.os.Build; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java index a3fa1a9c06cf..f0b4af6fc44f 100644 --- a/core/tests/coretests/src/android/content/pm/SignatureTest.java +++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java @@ -16,7 +16,7 @@ package android.content.pm; -import android.support.test.filters.LargeTest; +import androidx.test.filters.LargeTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java index 68942cbd54ad..f6527da0ba1a 100644 --- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java +++ b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java @@ -16,12 +16,12 @@ package android.content.pm; -import android.content.pm.VerificationParams; import android.net.Uri; import android.os.Parcel; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; +import androidx.test.filters.LargeTest; + /** * Tests the android.content.pm.VerificationParams class * diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java index 88d7a59a98e2..e7cd02d866d5 100644 --- a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java +++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java @@ -17,7 +17,8 @@ package android.content.pm; import android.os.Parcel; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import java.util.Random; diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java index e248a7771cab..1ca879cde6c8 100644 --- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java +++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java @@ -29,12 +29,21 @@ import android.content.pm.PackageParser.Package; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; +import libcore.testing.io.TestIoUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -45,14 +54,6 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import libcore.testing.io.TestIoUtils; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - @SmallTest @RunWith(AndroidJUnit4.class) public class DexMetadataHelperTest { diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java index 47554a68645a..47b14bbaa8fa 100644 --- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java +++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java @@ -17,9 +17,10 @@ package android.content.res; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.SmallTest; import android.util.TypedValue; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; import java.lang.reflect.InvocationTargetException; diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java index 72b9197089d7..2fc3e36e7948 100644 --- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java +++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java @@ -13,19 +13,18 @@ * License for the specific language governing permissions and limitations under * the License. */ -package android.content.res; -import org.junit.runner.RunWith; -import org.junit.Test; -import org.junit.runners.JUnit4; +package android.content.res; -import android.content.res.Configuration; -import android.support.test.filters.SmallTest; import android.platform.test.annotations.Presubmit; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; -import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java index 42ff2e94a24e..7ab9d7f4ffe1 100644 --- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java +++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.content.res; import static android.content.res.FontResourcesParser.FamilyResourceEntry; @@ -26,9 +27,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import android.app.Instrumentation; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java index e85666eb1639..aa1a5341de57 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java @@ -13,14 +13,15 @@ * License for the specific language governing permissions and limitations under * the License. */ + package android.content.res; import android.os.FileUtils; import android.os.LocaleList; -import android.support.test.filters.SmallTest; import android.test.AndroidTestCase; import android.util.DisplayMetrics; -import android.view.Display; + +import androidx.test.filters.SmallTest; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index b2ff9278d83b..a2dab996a8d7 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -13,18 +13,20 @@ * License for the specific language governing permissions and limitations under * the License. */ + package android.content.res; import android.annotation.NonNull; import android.app.ResourcesManager; import android.os.Binder; import android.os.LocaleList; -import android.support.test.filters.SmallTest; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Display; import android.view.DisplayAdjustments; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; public class ResourcesManagerTest extends TestCase { diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java index 075f5b7f1d6a..123da3e8703b 100644 --- a/core/tests/coretests/src/android/database/CursorWindowTest.java +++ b/core/tests/coretests/src/android/database/CursorWindowTest.java @@ -16,14 +16,14 @@ package android.database; -import android.test.suitebuilder.annotation.SmallTest; -import android.database.CursorWindow; import android.test.PerformanceTestCase; -import java.util.Arrays; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; +import java.util.Arrays; + public class CursorWindowTest extends TestCase implements PerformanceTestCase { public boolean isPerformanceOnly() { return false; diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java index 3507223b68ab..eb4fd70a1136 100644 --- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java +++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java @@ -18,10 +18,6 @@ package android.database; import android.content.ContentValues; import android.content.Context; -import android.database.Cursor; -import android.database.CursorIndexOutOfBoundsException; -import android.database.DataSetObserver; -import android.database.DatabaseUtils; import android.database.sqlite.SQLiteCursor; import android.database.sqlite.SQLiteCursorDriver; import android.database.sqlite.SQLiteDatabase; @@ -29,11 +25,12 @@ import android.database.sqlite.SQLiteQuery; import android.os.Looper; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.Log; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java index 9d75c784fe41..49fb75bf6a45 100644 --- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java +++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java @@ -25,16 +25,17 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDebug; import android.database.sqlite.SQLiteException; import android.os.Parcel; -import android.support.test.InstrumentationRegistry; import android.support.test.uiautomator.UiDevice; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.util.Pair; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; + import junit.framework.Assert; import java.io.File; diff --git a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java index b32829412cb5..ee7936fb8857 100644 --- a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java +++ b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java @@ -17,17 +17,17 @@ package android.database; import android.database.sqlite.SQLiteDatabase; -import android.database.Cursor; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; import android.test.MoreAsserts; +import android.util.Log; -import java.util.ArrayList; -import java.util.Locale; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.Locale; + public class DatabaseLocaleTest extends TestCase { private SQLiteDatabase mDatabase; diff --git a/core/tests/coretests/src/android/database/DatabaseLockTest.java b/core/tests/coretests/src/android/database/DatabaseLockTest.java index 8d3cf5a13124..e8936c98392e 100644 --- a/core/tests/coretests/src/android/database/DatabaseLockTest.java +++ b/core/tests/coretests/src/android/database/DatabaseLockTest.java @@ -17,11 +17,13 @@ package android.database; import android.database.sqlite.SQLiteDatabase; -import android.test.suitebuilder.annotation.Suppress; +import android.test.AndroidTestCase; import android.util.Log; + +import androidx.test.filters.Suppress; + import java.io.File; import java.util.concurrent.atomic.AtomicInteger; -import android.test.AndroidTestCase; /* * This is a series of unit tests for database locks. diff --git a/core/tests/coretests/src/android/database/DatabaseStatementTest.java b/core/tests/coretests/src/android/database/DatabaseStatementTest.java index 895d715c8cc0..4b34650183e3 100644 --- a/core/tests/coretests/src/android/database/DatabaseStatementTest.java +++ b/core/tests/coretests/src/android/database/DatabaseStatementTest.java @@ -17,14 +17,14 @@ package android.database; import android.content.Context; -import android.database.Cursor; import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDoneException; import android.database.sqlite.SQLiteStatement; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.MediumTest; import java.io.File; diff --git a/core/tests/coretests/src/android/database/DatabaseStressTest.java b/core/tests/coretests/src/android/database/DatabaseStressTest.java index 30e46e7dfb90..bfea1fc61eb7 100644 --- a/core/tests/coretests/src/android/database/DatabaseStressTest.java +++ b/core/tests/coretests/src/android/database/DatabaseStressTest.java @@ -17,11 +17,11 @@ package android.database; import android.content.Context; -import android.database.sqlite.*; +import android.database.sqlite.SQLiteDatabase; +import android.test.AndroidTestCase; import android.util.Log; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.Suppress; +import androidx.test.filters.Suppress; import java.io.File; diff --git a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java index 7c206d7eecf7..be156c8eb67a 100644 --- a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java +++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java @@ -20,7 +20,7 @@ import static android.database.DatabaseUtils.bindSelection; import static org.junit.Assert.assertEquals; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/database/RedactingCursorTest.java b/core/tests/coretests/src/android/database/RedactingCursorTest.java index 93998f3db05f..e2d2bae0e8b2 100644 --- a/core/tests/coretests/src/android/database/RedactingCursorTest.java +++ b/core/tests/coretests/src/android/database/RedactingCursorTest.java @@ -22,8 +22,9 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.net.Uri; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java index 9ed3f11b548f..730a3cb45856 100644 --- a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java +++ b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java @@ -26,11 +26,12 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabaseConfiguration; import android.database.sqlite.SQLiteDebug; import android.database.sqlite.SQLiteOpenHelper; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/database/run_newdb_perf_test.sh b/core/tests/coretests/src/android/database/run_newdb_perf_test.sh index c5b2c97b572f..95f1f83bea10 100755 --- a/core/tests/coretests/src/android/database/run_newdb_perf_test.sh +++ b/core/tests/coretests/src/android/database/run_newdb_perf_test.sh @@ -23,7 +23,7 @@ echo "Running benchmark $RUN_N times" for (( i=0; i<$RUN_N; i++ )) do - adb shell am instrument -e class 'android.database.NewDatabasePerformanceTestSuite' -w 'com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner' + adb shell am instrument -e class 'android.database.NewDatabasePerformanceTestSuite' -w 'com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner' done adb logcat -d > /tmp/testlogcat.txt diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java index 551a58ed7cb5..5dbcb3c46b50 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java @@ -22,9 +22,10 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.database.DatabaseUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Test; diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java index ed14a5348ab4..f1d27d4a13ab 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java @@ -21,11 +21,12 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.HandlerThread; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java index c52cf6ec5e5f..78d3c417dcf8 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java @@ -23,7 +23,8 @@ import android.database.Cursor; import android.database.CursorWindow; import android.platform.test.annotations.Presubmit; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.filters.LargeTest; import java.io.File; import java.util.Arrays; diff --git a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java index e9e2a4d098f0..564460e18294 100644 --- a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java +++ b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java @@ -17,14 +17,14 @@ package android.graphics; import android.os.ParcelFileDescriptor; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; - public class BitmapFactoryTest extends TestCase { // tests that we can decode bitmaps from MemoryFiles diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java index 3666dddb0782..e79d2ae1e194 100644 --- a/core/tests/coretests/src/android/graphics/BitmapTest.java +++ b/core/tests/coretests/src/android/graphics/BitmapTest.java @@ -16,11 +16,10 @@ package android.graphics; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; - public class BitmapTest extends TestCase { @SmallTest diff --git a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java index 6e38fb685887..8e9b38cc5a1f 100644 --- a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java +++ b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java @@ -19,8 +19,8 @@ package android.graphics; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/graphics/ColorStateListTest.java b/core/tests/coretests/src/android/graphics/ColorStateListTest.java index 374d1421bdbc..1d34f9383818 100644 --- a/core/tests/coretests/src/android/graphics/ColorStateListTest.java +++ b/core/tests/coretests/src/android/graphics/ColorStateListTest.java @@ -19,7 +19,8 @@ package android.graphics; import android.content.res.ColorStateList; import android.content.res.Resources; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java index 76267b23a0ca..1771671db26a 100644 --- a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java +++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java @@ -22,11 +22,12 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.fonts.FontFileUtil; import android.graphics.fonts.FontVariationAxis; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.util.Log; import android.util.Pair; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + import org.junit.Test; import java.io.File; diff --git a/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java b/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java index 164c1aa770bd..3cfeb2546940 100644 --- a/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java +++ b/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java @@ -20,7 +20,8 @@ import android.content.Context; import android.content.res.Resources; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.Suppress; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index b5ed01f708b1..bf56df1c9441 100644 --- a/core/tests/coretests/src/android/graphics/PaintTest.java +++ b/core/tests/coretests/src/android/graphics/PaintTest.java @@ -18,9 +18,9 @@ package android.graphics; import static org.junit.Assert.assertNotEquals; -import android.graphics.Paint; import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import java.util.Arrays; import java.util.HashSet; diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java index 950f8731bae1..6cc42f6a3efc 100644 --- a/core/tests/coretests/src/android/graphics/PathOffsetTest.java +++ b/core/tests/coretests/src/android/graphics/PathOffsetTest.java @@ -16,13 +16,13 @@ package android.graphics; +import static org.junit.Assert.assertTrue; import android.graphics.Bitmap.Config; import android.graphics.Path.Direction; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; -import static org.junit.Assert.assertTrue; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java index 78e4959da92a..c6d6d1ff90d5 100644 --- a/core/tests/coretests/src/android/graphics/PathTest.java +++ b/core/tests/coretests/src/android/graphics/PathTest.java @@ -16,11 +16,10 @@ package android.graphics; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; - public class PathTest extends TestCase { @SmallTest diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java index d31d7d54940c..2918f44ad65d 100644 --- a/core/tests/coretests/src/android/graphics/RectTest.java +++ b/core/tests/coretests/src/android/graphics/RectTest.java @@ -23,8 +23,9 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java index 909a8d97df51..e1ca7dfb7cc2 100644 --- a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java +++ b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java @@ -16,7 +16,7 @@ package android.graphics; -import android.test.suitebuilder.annotation.LargeTest; +import androidx.test.filters.LargeTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 6fdb71fcbab0..c66bac6cc335 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -27,12 +27,13 @@ import android.graphics.fonts.Font; import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.FontConfig; import android.util.ArrayMap; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index b0c7976f8014..2d16f826b243 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -22,13 +22,12 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java index 781e343e8139..3dc998709b8f 100644 --- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java +++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2017 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.graphics.drawable; import android.content.res.Resources; @@ -11,11 +27,12 @@ import android.graphics.Path.Direction; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; import android.util.Log; import android.util.PathParser; +import androidx.test.filters.LargeTest; + import org.junit.Test; import java.io.File; diff --git a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java index 655efb5239eb..d0a6ff9251cc 100644 --- a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java +++ b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java @@ -24,8 +24,9 @@ import android.graphics.ColorFilter; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Xfermode; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java index 64fadc03f0bb..2bdcc284154b 100644 --- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java +++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java @@ -25,9 +25,10 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Parcel; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; import java.io.ByteArrayOutputStream; diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java index f90ae340b2e9..0a25bd7e3f2c 100644 --- a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java +++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java @@ -23,8 +23,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java index dcc51e121b32..823fca5a68ad 100644 --- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java +++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java @@ -22,10 +22,11 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.Pair; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index f30b1a29c781..daf613976358 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -22,15 +22,12 @@ import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.drawable.ColorDrawable; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; import android.media.Image; import android.media.ImageReader; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; import android.util.DisplayMetrics; import android.util.Log; @@ -40,6 +37,8 @@ import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.widget.ImageView; +import androidx.test.filters.LargeTest; + import java.nio.ByteBuffer; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java new file mode 100644 index 000000000000..16be0b0a27c1 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import android.support.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SmallTest +@RunWith(JUnit4.class) +/** Tests for {@link HdmiUtils} class. */ +public class HdmiUtilsTest { + + @Test + public void pathToPort_isMe() { + int targetPhysicalAddress = 0x1000; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS); + } + + @Test + public void pathToPort_isDirectlyBelow() { + int targetPhysicalAddress = 0x1100; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_isBelow() { + int targetPhysicalAddress = 0x1110; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_neitherMeNorBelow() { + int targetPhysicalAddress = 0x3000; + int myPhysicalAddress = 0x2000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2200; + myPhysicalAddress = 0x3300; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2213; + myPhysicalAddress = 0x2212; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2340; + myPhysicalAddress = 0x2310; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + } +} diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java index 3be776deb9f1..aabfc2872269 100644 --- a/core/tests/coretests/src/android/metrics/LogMakerTest.java +++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.metrics; -import android.support.test.filters.LargeTest; +import androidx.test.filters.LargeTest; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; diff --git a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java index 784a12fa1f57..96dac648c01e 100644 --- a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java +++ b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.metrics; import android.metrics.MetricsReader.Event; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java index 1286b137cc1f..91722377df09 100644 --- a/core/tests/coretests/src/android/net/LocalSocketTest.java +++ b/core/tests/coretests/src/android/net/LocalSocketTest.java @@ -16,12 +16,9 @@ package android.net; -import android.net.Credentials; -import android.net.LocalServerSocket; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; import android.test.MoreAsserts; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java index fff23a04db9f..0f1c71d7c601 100644 --- a/core/tests/coretests/src/android/net/NetworkKeyTest.java +++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.net; import static org.junit.Assert.assertEquals; @@ -7,7 +23,8 @@ import static org.mockito.Mockito.when; import android.net.wifi.ScanResult; import android.net.wifi.WifiInfo; import android.net.wifi.WifiSsid; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java b/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java index c6758ce1950d..29e212f4839e 100644 --- a/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java +++ b/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.net; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java index ff9816ad090c..3e45a79951d3 100644 --- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java +++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package android.net; import static junit.framework.Assert.assertFalse; @@ -9,7 +25,8 @@ import static org.mockito.Matchers.eq; import android.Manifest.permission; import android.content.Context; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java index 5cbf02a79083..bc12e727c5f0 100644 --- a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java +++ b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java @@ -19,7 +19,7 @@ package android.net; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java index 11d066b3afb0..eec09e65a501 100644 --- a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java +++ b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java @@ -19,10 +19,10 @@ package android.net; import com.android.org.conscrypt.ClientSessionContext; import com.android.org.conscrypt.SSLClientSessionCache; -import org.mockito.Mockito; - import junit.framework.TestCase; +import org.mockito.Mockito; + import java.security.KeyManagementException; import java.security.SecureRandom; diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java index 109f32e1a48e..d984d86e1147 100644 --- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java +++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java @@ -16,11 +16,17 @@ package android.net; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.os.Bundle; import android.os.Parcel; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java index d72738c6e32c..87edb6e01425 100644 --- a/core/tests/coretests/src/android/net/SntpClientTest.java +++ b/core/tests/coretests/src/android/net/SntpClientTest.java @@ -20,9 +20,10 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; +import androidx.test.runner.AndroidJUnit4; + import libcore.util.HexEncoding; import org.junit.Before; diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index ea0347d67ad7..a33de7bb2d9d 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -18,7 +18,8 @@ package android.net; import android.content.ContentUris; import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/net/WebAddressTest.java b/core/tests/coretests/src/android/net/WebAddressTest.java index 6fcb97ea8b5f..70a62533d8e1 100644 --- a/core/tests/coretests/src/android/net/WebAddressTest.java +++ b/core/tests/coretests/src/android/net/WebAddressTest.java @@ -16,8 +16,8 @@ package android.net; -import android.net.WebAddress; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; public class WebAddressTest extends TestCase { diff --git a/core/tests/coretests/src/android/net/http/SslCertificateTest.java b/core/tests/coretests/src/android/net/http/SslCertificateTest.java index 6a30c6c5afdf..1beb1de0075c 100644 --- a/core/tests/coretests/src/android/net/http/SslCertificateTest.java +++ b/core/tests/coretests/src/android/net/http/SslCertificateTest.java @@ -17,12 +17,13 @@ package android.net.http; -import android.net.http.SslCertificate; -import android.test.suitebuilder.annotation.LargeTest; +import androidx.test.filters.LargeTest; + +import junit.framework.TestCase; + import java.io.ByteArrayInputStream; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import junit.framework.TestCase; public class SslCertificateTest extends TestCase { diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java index bf11d56de045..4c5141537c6a 100644 --- a/core/tests/coretests/src/android/os/AidlTest.java +++ b/core/tests/coretests/src/android/os/AidlTest.java @@ -16,11 +16,10 @@ package android.os; -import android.os.IInterface; -import android.os.Parcel; -import android.os.Parcelable; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; + import com.google.android.collect.Lists; + import junit.framework.TestCase; import java.util.List; diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java index 6cdb35abce5a..ce6ad87a310e 100644 --- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java +++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package android.os; import static org.junit.Assert.assertEquals; @@ -25,12 +24,13 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.util.Log; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.aidl.IBpcCallbackObserver; import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService; import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService; @@ -63,7 +63,7 @@ import java.util.function.Consumer; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class android.os.BinderProxyCountingTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner * * or * diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java index 4c36b5c359a2..aceda2d0524b 100644 --- a/core/tests/coretests/src/android/os/BinderProxyTest.java +++ b/core/tests/coretests/src/android/os/BinderProxyTest.java @@ -19,7 +19,8 @@ package android.os; import android.annotation.Nullable; import android.content.Context; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.MediumTest; public class BinderProxyTest extends AndroidTestCase { private static class CountingListener implements Binder.ProxyTransactListener { diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java index 6c9c3c111ff8..a354195c75a3 100644 --- a/core/tests/coretests/src/android/os/BinderTest.java +++ b/core/tests/coretests/src/android/os/BinderTest.java @@ -16,7 +16,7 @@ package android.os; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityService.java b/core/tests/coretests/src/android/os/BinderThreadPriorityService.java index 47a4483b8a29..ed42058d3ef4 100644 --- a/core/tests/coretests/src/android/os/BinderThreadPriorityService.java +++ b/core/tests/coretests/src/android/os/BinderThreadPriorityService.java @@ -18,7 +18,6 @@ package android.os; import android.app.Service; import android.content.Intent; -import android.text.TextUtils; import android.util.Log; /** diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java index 56e977c7d687..48c9df65da61 100644 --- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java +++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.io.File; diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java index 5664df6e9744..e16a3dbe4a26 100644 --- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java +++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java @@ -24,9 +24,10 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java index fabcf3d920a9..5a3724f7aa24 100644 --- a/core/tests/coretests/src/android/os/BrightnessLimit.java +++ b/core/tests/coretests/src/android/os/BrightnessLimit.java @@ -18,7 +18,6 @@ package android.os; import android.app.Activity; import android.hardware.display.DisplayManager; -import android.os.Bundle; import android.provider.Settings; import android.view.View; import android.view.View.OnClickListener; diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java index 551ea8d7d5d4..b4c47af93355 100644 --- a/core/tests/coretests/src/android/os/BroadcasterTest.java +++ b/core/tests/coretests/src/android/os/BroadcasterTest.java @@ -16,11 +16,8 @@ package android.os; -import android.os.Broadcaster; -import android.os.Handler; -import android.os.Message; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.MediumTest; + import junit.framework.TestCase; public class BroadcasterTest extends TestCase { diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index 37586279d980..decc76869a53 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -16,9 +16,8 @@ package android.os; -import android.os.Build; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; +import androidx.test.filters.SmallTest; + import junit.framework.Assert; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index 9fcf96d6f3ae..e4dc99347802 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -20,8 +20,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java index 5189df574a33..d98ceaf57dd9 100644 --- a/core/tests/coretests/src/android/os/EnvironmentTest.java +++ b/core/tests/coretests/src/android/os/EnvironmentTest.java @@ -25,8 +25,9 @@ import static android.os.Environment.classifyExternalStorageDirectory; import static org.junit.Assert.assertEquals; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java index 93e27af44170..ece7645b7389 100644 --- a/core/tests/coretests/src/android/os/FileObserverTest.java +++ b/core/tests/coretests/src/android/os/FileObserverTest.java @@ -16,13 +16,14 @@ package android.os; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; - import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; +import androidx.test.filters.MediumTest; + +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; + import java.io.File; import java.io.FileOutputStream; import java.util.Iterator; diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 514ea0cc013e..5bce227cdf48 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -51,8 +51,9 @@ import static org.junit.Assert.fail; import android.content.Context; import android.os.FileUtils.MemoryPipe; import android.provider.DocumentsContract.Document; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import libcore.io.Streams; diff --git a/core/tests/coretests/src/android/os/HandlerTester.java b/core/tests/coretests/src/android/os/HandlerTester.java index a216a0bf7169..fa442f474d6e 100644 --- a/core/tests/coretests/src/android/os/HandlerTester.java +++ b/core/tests/coretests/src/android/os/HandlerTester.java @@ -16,10 +16,6 @@ package android.os; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; - public abstract class HandlerTester extends Thread { public abstract void go(); public abstract void handleMessage(Message msg); diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java index 9772aa427999..93cfc40d555e 100644 --- a/core/tests/coretests/src/android/os/HandlerThreadTest.java +++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java @@ -16,13 +16,9 @@ package android.os; +import androidx.test.filters.MediumTest; + import junit.framework.TestCase; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; -import android.os.Process; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; public class HandlerThreadTest extends TestCase { private static final int TEST_WHAT = 1; diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java index 6c0a862aa2ea..d8886c97838e 100644 --- a/core/tests/coretests/src/android/os/IdleHandlerTest.java +++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java @@ -16,11 +16,10 @@ package android.os; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.MessageQueue.IdleHandler; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.MediumTest; + import junit.framework.TestCase; public class IdleHandlerTest extends TestCase { diff --git a/core/tests/coretests/src/android/os/LocaleListTest.java b/core/tests/coretests/src/android/os/LocaleListTest.java index 17ef7730f452..1f00a7a12c54 100644 --- a/core/tests/coretests/src/android/os/LocaleListTest.java +++ b/core/tests/coretests/src/android/os/LocaleListTest.java @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.os; -import android.test.suitebuilder.annotation.SmallTest; +package android.os; -import java.util.Locale; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; +import java.util.Locale; + public class LocaleListTest extends TestCase { @SmallTest public void testConstructor() throws Exception { diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java index 20b298d639d2..05c2995fa158 100644 --- a/core/tests/coretests/src/android/os/MemoryFileTest.java +++ b/core/tests/coretests/src/android/os/MemoryFileTest.java @@ -17,8 +17,9 @@ package android.os; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.SmallTest; import java.io.IOException; import java.io.InputStream; diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java index 1cd1020bfe3a..2c5588e6ed0d 100644 --- a/core/tests/coretests/src/android/os/MessageQueueTest.java +++ b/core/tests/coretests/src/android/os/MessageQueueTest.java @@ -16,11 +16,9 @@ package android.os; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + import junit.framework.TestCase; @Suppress // Failing. diff --git a/core/tests/coretests/src/android/os/MessengerService.java b/core/tests/coretests/src/android/os/MessengerService.java index f15e13408084..db3347197d90 100644 --- a/core/tests/coretests/src/android/os/MessengerService.java +++ b/core/tests/coretests/src/android/os/MessengerService.java @@ -18,11 +18,6 @@ package android.os; import android.app.Service; import android.content.Intent; -import android.os.RemoteException; -import android.os.IBinder; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; public class MessengerService extends Service { private final Handler mHandler = new Handler() { diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java index 473ffe2298b3..9143ff1d1017 100644 --- a/core/tests/coretests/src/android/os/MessengerTest.java +++ b/core/tests/coretests/src/android/os/MessengerTest.java @@ -20,13 +20,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.MediumTest; public class MessengerTest extends AndroidTestCase { private Messenger mServiceMessenger; diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java index 2b841269e5ae..08fb945857e9 100644 --- a/core/tests/coretests/src/android/os/OsTests.java +++ b/core/tests/coretests/src/android/os/OsTests.java @@ -16,12 +16,8 @@ package android.os; -import com.google.android.collect.Lists; import junit.framework.TestSuite; -import java.util.Enumeration; -import java.util.List; - public class OsTests { public static TestSuite suite() { TestSuite suite = new TestSuite(OsTests.class.getName()); diff --git a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java index 516dc0ab135c..a6b296d37f82 100644 --- a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java +++ b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java @@ -20,10 +20,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; import android.util.ArrayMap; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/os/PatternMatcherTest.java b/core/tests/coretests/src/android/os/PatternMatcherTest.java index 9645ccc11b76..82350cd5ffdf 100644 --- a/core/tests/coretests/src/android/os/PatternMatcherTest.java +++ b/core/tests/coretests/src/android/os/PatternMatcherTest.java @@ -1,9 +1,27 @@ +/* + * 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. + */ + package android.os; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; -import org.junit.runner.RunWith; + import org.junit.Test; +import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java index 7533c84673de..38ad90f11a23 100644 --- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java +++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java @@ -17,15 +17,16 @@ package android.os; import android.os.PerformanceCollector.PerformanceResultsWriter; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; + +import junit.framework.TestCase; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Random; -import junit.framework.TestCase; - public class PerformanceCollectorTest extends TestCase { private PerformanceCollector mPerfCollector; diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java index a828f4418515..1b587dd92db0 100644 --- a/core/tests/coretests/src/android/os/PowerManagerTest.java +++ b/core/tests/coretests/src/android/os/PowerManagerTest.java @@ -21,10 +21,11 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.support.test.uiautomator.UiDevice; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; import org.junit.After; import org.junit.Test; diff --git a/core/tests/coretests/src/android/os/PowerManagerVrTest.java b/core/tests/coretests/src/android/os/PowerManagerVrTest.java index e01e5fa4faee..5d2c65b0153c 100644 --- a/core/tests/coretests/src/android/os/PowerManagerVrTest.java +++ b/core/tests/coretests/src/android/os/PowerManagerVrTest.java @@ -19,16 +19,14 @@ package android.os; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.os.PowerManager; -import android.os.RemoteException; -import android.os.ServiceManager; import android.provider.Settings; -import android.service.dreams.IDreamManager; import android.service.dreams.DreamService; -import android.support.test.filters.MediumTest; -import android.support.test.filters.SmallTest; +import android.service.dreams.IDreamManager; import android.test.ActivityInstrumentationTestCase2; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; + /** * Tests dream aspects of PowerManager. */ diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java index 1f5b7c829831..b749e715316a 100644 --- a/core/tests/coretests/src/android/os/ProcessTest.java +++ b/core/tests/coretests/src/android/os/ProcessTest.java @@ -17,14 +17,10 @@ package android.os; -import android.os.Process; -import android.os.UserHandle; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.MediumTest; import junit.framework.TestCase; - public class ProcessTest extends TestCase { @MediumTest diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java index 9e1523165925..d5163e193510 100644 --- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java +++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java @@ -24,10 +24,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; import android.system.Os; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java index 9e445541ad3a..8085993389bb 100644 --- a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java +++ b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.os; import android.app.ActivityManager; @@ -20,10 +21,10 @@ import android.app.VrManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; -import android.os.Process; import android.provider.Settings; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; /** * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's diff --git a/core/tests/coretests/src/android/os/TestHandlerThread.java b/core/tests/coretests/src/android/os/TestHandlerThread.java index 7e84af363a86..8c0330b4bb45 100644 --- a/core/tests/coretests/src/android/os/TestHandlerThread.java +++ b/core/tests/coretests/src/android/os/TestHandlerThread.java @@ -16,11 +16,6 @@ package android.os; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.MessageQueue.IdleHandler; - abstract class TestHandlerThread { private boolean mDone = false; private boolean mSuccess = false; diff --git a/core/tests/coretests/src/android/os/TestVrActivity.java b/core/tests/coretests/src/android/os/TestVrActivity.java index 33ff1645b37b..75df7c22fe65 100644 --- a/core/tests/coretests/src/android/os/TestVrActivity.java +++ b/core/tests/coretests/src/android/os/TestVrActivity.java @@ -17,7 +17,6 @@ package android.os; import android.app.Activity; -import android.os.Bundle; import android.service.vr.VrListenerService; import java.util.concurrent.CountDownLatch; diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java index 1541553f36f1..5cad549d39bf 100644 --- a/core/tests/coretests/src/android/os/TraceTest.java +++ b/core/tests/coretests/src/android/os/TraceTest.java @@ -16,13 +16,13 @@ package android.os; -import android.os.Debug; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.Log; +import androidx.test.filters.LargeTest; +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; + /** * This class is used to test the native tracing support. Run this test * while tracing on the emulator and then run traceview to view the trace. diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java index af559fddd48a..4a1cdbfe3114 100644 --- a/core/tests/coretests/src/android/os/UserHandleTest.java +++ b/core/tests/coretests/src/android/os/UserHandleTest.java @@ -25,7 +25,7 @@ import static android.os.UserHandle.getUserId; import static org.junit.Assert.assertEquals; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java index 44510c2379f5..af3660a6dab9 100644 --- a/core/tests/coretests/src/android/os/VintfObjectTest.java +++ b/core/tests/coretests/src/android/os/VintfObjectTest.java @@ -16,7 +16,6 @@ package android.os; -import junit.framework.Assert; import junit.framework.TestCase; public class VintfObjectTest extends TestCase { diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java index 3ec297c27a2a..62f2ac28a601 100644 --- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java +++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java @@ -19,7 +19,8 @@ package android.os.storage; import android.os.ParcelFileDescriptor; import android.os.ProxyFileDescriptorCallback; import android.system.ErrnoException; -import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.filters.LargeTest; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/preference/ListPreferenceTest.java b/core/tests/coretests/src/android/preference/ListPreferenceTest.java index 72f62f167989..51dbb64244fc 100644 --- a/core/tests/coretests/src/android/preference/ListPreferenceTest.java +++ b/core/tests/coretests/src/android/preference/ListPreferenceTest.java @@ -16,10 +16,10 @@ package android.preference; -import android.preference.ListPreference; -import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; +import androidx.test.filters.LargeTest; + @LargeTest public class ListPreferenceTest extends AndroidTestCase { public void testListPreferenceSummaryFromEntries() { diff --git a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java index 654474cba699..0deb77e60a51 100644 --- a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java +++ b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java @@ -16,20 +16,20 @@ package android.preference; - import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java index 5d12f7e43558..94d85e63cc5f 100644 --- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java +++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java @@ -41,9 +41,10 @@ import android.print.test.services.PrintServiceCallbacks; import android.print.test.services.PrinterDiscoverySessionCallbacks; import android.print.test.services.StubbablePrinterDiscoverySession; import android.printservice.recommendation.IRecommendationsChangeListener; -import android.support.test.filters.LargeTest; -import android.support.test.filters.MediumTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index 800b86418163..17e9654b651a 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -28,9 +28,10 @@ import android.content.ContentResolver; import android.os.Bundle; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Test; diff --git a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java index 1465d0a98394..02a9adf4fb4d 100644 --- a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java +++ b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java @@ -20,9 +20,10 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.net.Uri; import android.provider.DocumentsContract.Path; -import android.support.test.filters.SmallTest; import android.test.ProviderTestCase2; +import androidx.test.filters.SmallTest; + import java.util.Arrays; /** diff --git a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java index 29558696436d..7e02be85f01a 100644 --- a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java +++ b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java @@ -25,25 +25,25 @@ import static org.junit.Assert.assertSame; import android.app.Instrumentation; import android.content.Context; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.graphics.Typeface; import android.os.Handler; -import android.provider.FontsContract.Columns; import android.provider.FontsContract.FontFamilyResult; -import android.provider.FontsContract.FontInfo; -import android.provider.FontsContract; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; -import java.util.ArrayList; -import java.util.List; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; + @SmallTest @RunWith(AndroidJUnit4.class) public class FontsContractE2ETest { diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java index d42d79e8fd82..c5d6f7fad3e8 100644 --- a/core/tests/coretests/src/android/provider/FontsContractTest.java +++ b/core/tests/coretests/src/android/provider/FontsContractTest.java @@ -13,31 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.provider; +import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND; +import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE; +import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY; +import static android.provider.FontsContract.Columns.RESULT_CODE_OK; + import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static android.provider.FontsContract.Columns.RESULT_CODE_OK; -import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND; -import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE; -import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY; - import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; import android.content.pm.Signature; import android.database.MatrixCursor; import android.graphics.fonts.FontVariationAxis; import android.provider.FontsContract.FontInfo; -import android.support.test.filters.SmallTest; import android.test.ProviderTestCase2; import android.util.Base64; +import androidx.test.filters.SmallTest; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/core/tests/coretests/src/android/provider/MockFontProvider.java b/core/tests/coretests/src/android/provider/MockFontProvider.java index ad5b130ca90d..e527dec13baf 100644 --- a/core/tests/coretests/src/android/provider/MockFontProvider.java +++ b/core/tests/coretests/src/android/provider/MockFontProvider.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.provider; import static android.provider.FontsContract.Columns; @@ -21,16 +22,13 @@ import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; -import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.database.Cursor; import android.database.MatrixCursor; -import android.graphics.fonts.FontVariationAxis; import android.net.Uri; -import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; -import android.util.ArraySet; -import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; import java.io.File; import java.io.FileNotFoundException; @@ -46,8 +44,6 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import com.android.internal.annotations.GuardedBy; - public class MockFontProvider extends ContentProvider { final static String AUTHORITY = "android.provider.fonts.font"; diff --git a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java index 7458de5fd310..f84355f6755e 100644 --- a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java +++ b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java @@ -21,9 +21,9 @@ import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.test.ProviderTestCase2; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; /** * ProviderTestCase that performs unit tests of SearchRecentSuggestionsProvider. diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 1ed5ce4a3929..93af0130d74c 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -27,9 +27,9 @@ import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; import android.platform.test.annotations.Presubmit; -import android.provider.Settings.Global; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -270,6 +270,7 @@ public class SettingsBackupTest { Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS, Settings.Global.GNSS_SATELLITE_BLACKLIST, Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS, + Settings.Global.HDMI_CEC_SWITCH_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, @@ -477,9 +478,10 @@ public class SettingsBackupTest { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, + Settings.Global.GUP_DEV_ALL_APPS, Settings.Global.GUP_DEV_OPT_IN_APPS, Settings.Global.GUP_DEV_OPT_OUT_APPS, - Settings.Global.GUP_BLACK_LIST, + Settings.Global.GUP_BLACKLIST, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, @@ -554,8 +556,10 @@ public class SettingsBackupTest { Settings.Global.APPOP_HISTORY_PARAMETERS, Settings.Global.APPOP_HISTORY_MODE, Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER, - Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS); - + Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS, + Settings.Global.ENABLE_RADIO_BUG_DETECTION, + Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD, + Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD); private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS = newHashSet( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, @@ -664,6 +668,7 @@ public class SettingsBackupTest { Settings.Secure.SMS_DEFAULT_APPLICATION, Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q Settings.Secure.TRUST_AGENTS_INITIALIZED, + Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS, Settings.Secure.TV_INPUT_CUSTOM_LABELS, Settings.Secure.TV_INPUT_HIDDEN_INPUTS, Settings.Secure.TV_USER_SETUP_COMPLETE, diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java index cb6f0e692082..a5f5f6733237 100644 --- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java +++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java @@ -34,9 +34,10 @@ import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; import java.util.HashMap; import java.util.List; diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java index b0d29bd1d8f0..08f9de602684 100644 --- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java +++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java @@ -22,8 +22,9 @@ import static org.junit.Assert.fail; import android.platform.test.annotations.Presubmit; import android.provider.SettingsValidators.Validator; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/provider/SmsProviderTest.java b/core/tests/coretests/src/android/provider/SmsProviderTest.java index af4d1a66053a..67ac8ea8d49b 100644 --- a/core/tests/coretests/src/android/provider/SmsProviderTest.java +++ b/core/tests/coretests/src/android/provider/SmsProviderTest.java @@ -22,8 +22,9 @@ import android.database.Cursor; import android.net.Uri; import android.provider.Telephony.Sms; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.Suppress; import java.util.GregorianCalendar; diff --git a/core/tests/coretests/src/android/provider/TestFontsProvider.java b/core/tests/coretests/src/android/provider/TestFontsProvider.java index 46906dfa1284..c3457faf9af5 100644 --- a/core/tests/coretests/src/android/provider/TestFontsProvider.java +++ b/core/tests/coretests/src/android/provider/TestFontsProvider.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.provider; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; @@ -23,7 +24,6 @@ import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.database.MatrixCursor; -import android.graphics.Typeface; import android.net.Uri; import android.os.ParcelFileDescriptor; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java index 0c9c4c17171c..ce0bf30473f6 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java index 61ab1526abc4..2a962c222371 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.google.common.collect.Lists; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java index b6af9bbd772a..2b37b52c8bf9 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java index dd8cd8dbd0c6..3b8f71585099 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java index a5a3ca90d9ac..2b15d73b6f01 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java @@ -20,8 +20,8 @@ import static android.security.keystore.recovery.TrustedRootCertificates.getRoot import static org.junit.Assert.assertTrue; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java index 15afbddf6f02..8522e6203284 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java index 7f0eb43f23be..17486d51d1a1 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java @@ -21,8 +21,8 @@ import static android.security.keystore.recovery.X509CertificateParsingUtils.dec import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java index e69d1e7505d1..d00d052db590 100644 --- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java +++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.service.euicc; import static org.junit.Assert.assertArrayEquals; @@ -23,10 +24,11 @@ import static org.junit.Assert.assertTrue; import android.os.Parcel; import android.service.carrier.CarrierIdentifier; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.telephony.UiccAccessRule; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java index 8a3ba8cc625b..4aa10007bf53 100644 --- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java +++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.service.notification; import static junit.framework.Assert.assertEquals; @@ -28,9 +29,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.metrics.LogMaker; import android.os.UserHandle; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; diff --git a/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java b/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java index ab541a15cdcc..9c8e86834bf6 100644 --- a/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java +++ b/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java @@ -16,16 +16,12 @@ package android.service.settings.suggestions; -import android.support.annotation.VisibleForTesting; - import java.util.ArrayList; import java.util.List; public class MockSuggestionService extends SuggestionService { - @VisibleForTesting static boolean sOnSuggestionLaunchedCalled; - @VisibleForTesting static boolean sOnSuggestionDismissedCalled; public static void reset() { diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java index dca8c096dc59..64edda5ee879 100644 --- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java +++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java @@ -21,10 +21,11 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.rule.ServiceTestRule; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.rule.ServiceTestRule; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java index b0ec55d946e2..8c47fcbc8947 100644 --- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java +++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java @@ -24,9 +24,10 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.os.Parcel; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java index 0c7e730e78e4..1c5986a838fc 100644 --- a/core/tests/coretests/src/android/text/AndroidCharacterTest.java +++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java @@ -19,7 +19,8 @@ package android.text; import static org.junit.Assert.assertArrayEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Test; diff --git a/core/tests/coretests/src/android/text/BidiFormatterTest.java b/core/tests/coretests/src/android/text/BidiFormatterTest.java index 1b936c7b1c1d..312fb68bbfc2 100644 --- a/core/tests/coretests/src/android/text/BidiFormatterTest.java +++ b/core/tests/coretests/src/android/text/BidiFormatterTest.java @@ -19,8 +19,9 @@ package android.text; import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java index c69f4f58b44e..cca1ad3a6dec 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java @@ -22,8 +22,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java index ea954f65476d..699243b3f2b9 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java @@ -27,11 +27,12 @@ import static org.junit.Assert.assertTrue; import android.graphics.Canvas; import android.graphics.Paint; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.style.ReplacementSpan; import android.util.ArraySet; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java index 313f1b6a6614..194899415e0d 100644 --- a/core/tests/coretests/src/android/text/EmojiTest.java +++ b/core/tests/coretests/src/android/text/EmojiTest.java @@ -21,8 +21,9 @@ import static org.junit.Assert.assertTrue; import android.icu.lang.UCharacterDirection; import android.icu.text.Bidi; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 5592aac5a7c0..cc51ec3da1fd 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -24,9 +24,10 @@ import android.graphics.fonts.Font; import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; -import android.support.test.InstrumentationRegistry; import android.util.ArrayMap; +import androidx.test.InstrumentationRegistry; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java index 1208d7ca194a..96e7fb9c8b16 100644 --- a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java +++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java @@ -22,12 +22,13 @@ import android.content.Context; import android.graphics.Path; import android.graphics.Typeface; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.method.MetaKeyKeyListener; import android.view.KeyEvent; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 97937a45825e..990161a88c22 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -31,11 +31,12 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; import android.text.style.StrikethroughSpan; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java index a0dca2c0dc4f..57bb4349c4fd 100644 --- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java +++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java @@ -23,9 +23,10 @@ import static org.junit.Assert.assertNull; import android.content.Context; import android.graphics.Typeface; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java index d9dc6fc44495..ba15b92012a7 100644 --- a/core/tests/coretests/src/android/text/PackedIntVectorTest.java +++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java @@ -19,8 +19,9 @@ package android.text; import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java index d0f2d46f9c2b..91b8c6ad0c53 100644 --- a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java +++ b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java @@ -16,18 +16,18 @@ package android.text; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.style.BulletSpan; import android.text.style.QuoteSpan; import android.text.style.SubscriptSpan; import android.text.style.UnderlineSpan; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java index 6c05601a31fd..9149f7b1b216 100644 --- a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java +++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java @@ -21,11 +21,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import android.annotation.NonNull; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.style.QuoteSpan; import android.text.style.UnderlineSpan; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java index f3684286dbbb..d248a1f69030 100644 --- a/core/tests/coretests/src/android/text/SpannableTest.java +++ b/core/tests/coretests/src/android/text/SpannableTest.java @@ -19,10 +19,11 @@ package android.text; import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.test.MoreAsserts; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java index 380e3153ccda..ca4373361fa5 100644 --- a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java +++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java @@ -21,11 +21,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import android.annotation.NonNull; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.style.QuoteSpan; import android.text.style.UnderlineSpan; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java index 573757165101..3ab075509373 100644 --- a/core/tests/coretests/src/android/text/SpannedTest.java +++ b/core/tests/coretests/src/android/text/SpannedTest.java @@ -21,13 +21,14 @@ import static org.junit.Assert.assertEquals; import android.graphics.Typeface; import android.os.Parcel; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.style.CharacterStyle; import android.text.style.StyleSpan; import android.text.style.TextAppearanceSpan; import android.text.style.TypefaceSpan; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java index bf0d42788b72..32370b3e6e05 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java @@ -19,10 +19,11 @@ package android.text; import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java index 5cf54268e004..4221ac208948 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java @@ -19,11 +19,12 @@ package android.text; import static org.junit.Assert.fail; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Directions; import android.text.StaticLayoutTest.LayoutBuilder; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java index 25217125bbf4..b1c896ec2ce4 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java @@ -25,13 +25,14 @@ import android.graphics.Canvas; import android.graphics.Paint.FontMetricsInt; import android.os.LocaleList; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; import android.text.method.EditorState; import android.text.style.LocaleSpan; import android.util.Log; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java index f6888e3adb75..0d42326d4bdb 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java @@ -19,10 +19,11 @@ package android.text; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java index 24020ceb0cd4..15fbc9e59508 100644 --- a/core/tests/coretests/src/android/text/TextLayoutTest.java +++ b/core/tests/coretests/src/android/text/TextLayoutTest.java @@ -17,8 +17,9 @@ package android.text; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java index 2fe882c8f9f8..90ce305b3dab 100644 --- a/core/tests/coretests/src/android/text/TextLineTest.java +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -25,14 +25,15 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.filters.Suppress; -import android.support.test.runner.AndroidJUnit4; import android.text.Layout.TabStops; import android.text.style.ReplacementSpan; import android.text.style.TabStopSpan; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index 72290bf10f9d..be6ef195fa96 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -26,15 +26,16 @@ import static org.junit.Assert.fail; import android.os.Parcel; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.LargeTest; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.test.MoreAsserts; import android.text.style.StyleSpan; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; import android.view.View; +import androidx.test.filters.LargeTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.google.android.collect.Lists; import org.junit.Test; diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java index fbd2412a35cc..0afe811f1ad6 100644 --- a/core/tests/coretests/src/android/text/VariationParserTest.java +++ b/core/tests/coretests/src/android/text/VariationParserTest.java @@ -21,8 +21,9 @@ import static org.junit.Assert.fail; import android.graphics.fonts.FontVariationAxis; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java index 9000ed07d606..5a0a84db5905 100644 --- a/core/tests/coretests/src/android/text/format/DateFormatTest.java +++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java index 872b71a5faa6..381c0512c532 100644 --- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java +++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java @@ -22,8 +22,9 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.LocaleList; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index 82e4bff200fd..068d04798858 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -25,11 +25,12 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.format.Formatter.BytesResult; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java index d563f2e2383b..ac0041119640 100644 --- a/core/tests/coretests/src/android/text/format/TimeTest.java +++ b/core/tests/coretests/src/android/text/format/TimeTest.java @@ -21,12 +21,13 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.filters.Suppress; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; import android.util.TimeFormatException; +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java index df4609f5a728..ddae652cec05 100644 --- a/core/tests/coretests/src/android/text/method/BackspaceTest.java +++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java @@ -17,15 +17,16 @@ package android.text.method; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.InputType; import android.util.KeyUtils; import android.view.KeyEvent; import android.widget.EditText; import android.widget.TextView.BufferType; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/method/EditorState.java b/core/tests/coretests/src/android/text/method/EditorState.java index 12bb8c839e60..4eff7a49ac7b 100644 --- a/core/tests/coretests/src/android/text/method/EditorState.java +++ b/core/tests/coretests/src/android/text/method/EditorState.java @@ -21,8 +21,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.graphics.Canvas; -import android.graphics.Paint; import android.text.Editable; import android.text.Spannable; import android.text.SpannableString; diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java index 45a501077128..45b63e03c27e 100644 --- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java +++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java @@ -17,15 +17,16 @@ package android.text.method; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.InputType; import android.util.KeyUtils; import android.view.KeyEvent; import android.widget.EditText; import android.widget.TextView.BufferType; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java index 684586387255..cc345f5785df 100644 --- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java +++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java @@ -22,8 +22,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java index 3d7e5bab15f3..a0d2f856d093 100644 --- a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java +++ b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java @@ -16,17 +16,17 @@ package android.text.style; - import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.SpannableString; import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java index be3a0be079bf..107ecd716b5a 100644 --- a/core/tests/coretests/src/android/text/util/LinkifyTest.java +++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java @@ -23,9 +23,6 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.Configuration; import android.os.LocaleList; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.Spannable; import android.text.SpannableString; import android.text.method.LinkMovementMethod; @@ -33,6 +30,10 @@ import android.text.style.URLSpan; import android.util.Patterns; import android.widget.TextView; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/transition/AutoTransitionTest.java b/core/tests/coretests/src/android/transition/AutoTransitionTest.java index 834fb7aa72a5..deae967a3e72 100644 --- a/core/tests/coretests/src/android/transition/AutoTransitionTest.java +++ b/core/tests/coretests/src/android/transition/AutoTransitionTest.java @@ -16,16 +16,16 @@ package android.transition; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + @RunWith(AndroidJUnit4.class) public class AutoTransitionTest { @Test diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java index 22365bac64a2..2077d9443e7a 100644 --- a/core/tests/coretests/src/android/transition/FadeTransitionTest.java +++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java @@ -22,13 +22,14 @@ import android.app.Activity; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.SmallTest; import android.transition.Transition.TransitionListener; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; import java.util.concurrent.CountDownLatch; diff --git a/core/tests/coretests/src/android/transition/SlideTransitionTest.java b/core/tests/coretests/src/android/transition/SlideTransitionTest.java index 8b9ec74be8df..046e49dffab1 100644 --- a/core/tests/coretests/src/android/transition/SlideTransitionTest.java +++ b/core/tests/coretests/src/android/transition/SlideTransitionTest.java @@ -19,18 +19,18 @@ package android.transition; import android.animation.AnimatorSetActivity; import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.SmallTest; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> { Activity mActivity; diff --git a/core/tests/coretests/src/android/transition/TransitionTest.java b/core/tests/coretests/src/android/transition/TransitionTest.java index 7e629f944684..f59c406f7f74 100644 --- a/core/tests/coretests/src/android/transition/TransitionTest.java +++ b/core/tests/coretests/src/android/transition/TransitionTest.java @@ -19,7 +19,6 @@ package android.transition; import android.animation.AnimatorSetActivity; import android.app.Activity; import android.graphics.Rect; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase2; import android.transition.Transition.EpicenterCallback; import android.util.ArrayMap; @@ -27,6 +26,8 @@ import android.view.View; import android.view.animation.AccelerateInterpolator; import android.widget.TextView; +import androidx.test.filters.LargeTest; + import com.android.frameworks.coretests.R; import java.lang.reflect.Field; diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java index f0cc7f77fe63..b212cf6b0803 100644 --- a/core/tests/coretests/src/android/util/ArrayMapTest.java +++ b/core/tests/coretests/src/android/util/ArrayMapTest.java @@ -14,10 +14,10 @@ package android.util; -import android.support.test.filters.LargeTest; -import android.util.ArrayMap; +import androidx.test.filters.LargeTest; import junit.framework.TestCase; + import org.junit.Test; import java.util.ConcurrentModificationException; diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java index af608c3ae259..15c51af0b3e1 100644 --- a/core/tests/coretests/src/android/util/Base64Test.java +++ b/core/tests/coretests/src/android/util/Base64Test.java @@ -16,7 +16,7 @@ package android.util; -import android.support.test.filters.LargeTest; +import androidx.test.filters.LargeTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/coretests/src/android/util/DataUnitTest.java index 4eae8b45e94b..ec296b76c374 100644 --- a/core/tests/coretests/src/android/util/DataUnitTest.java +++ b/core/tests/coretests/src/android/util/DataUnitTest.java @@ -16,7 +16,7 @@ package android.util; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java index 4c5ad76d8193..572e9b062097 100644 --- a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java +++ b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java @@ -16,10 +16,11 @@ package android.util; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; import java.util.Calendar; -import android.test.suitebuilder.annotation.SmallTest; /** * Unit tests for {@link DayOfMonthCursor}. diff --git a/core/tests/coretests/src/android/util/InternalSelectionView.java b/core/tests/coretests/src/android/util/InternalSelectionView.java index a0fb0f18a4fa..4a1baef8077a 100644 --- a/core/tests/coretests/src/android/util/InternalSelectionView.java +++ b/core/tests/coretests/src/android/util/InternalSelectionView.java @@ -16,19 +16,16 @@ package android.util; -import com.android.frameworks.coretests.R; - -import android.view.View; -import android.view.KeyEvent; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Paint; import android.graphics.Canvas; -import android.graphics.Rect; import android.graphics.Color; -import android.util.AttributeSet; - +import android.graphics.Paint; +import android.graphics.Rect; +import android.view.KeyEvent; +import android.view.View; +import com.android.frameworks.coretests.R; /** * A view that has a known number of selectable rows, and maintains a notion of which diff --git a/core/tests/coretests/src/android/util/KeyUtils.java b/core/tests/coretests/src/android/util/KeyUtils.java index 593f72742ea6..612643e12db0 100644 --- a/core/tests/coretests/src/android/util/KeyUtils.java +++ b/core/tests/coretests/src/android/util/KeyUtils.java @@ -17,16 +17,10 @@ package android.util; import android.app.Instrumentation; -import android.os.SystemClock; import android.test.ActivityInstrumentationTestCase; -import android.test.InstrumentationTestCase; -import android.view.Gravity; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; import android.view.ViewConfiguration; -import android.view.ViewGroup; /** * Reusable methods for generating key events. diff --git a/core/tests/coretests/src/android/util/KeyValueListParserTest.java b/core/tests/coretests/src/android/util/KeyValueListParserTest.java index 038f0b7efaa3..f65c4c7c0bdf 100644 --- a/core/tests/coretests/src/android/util/KeyValueListParserTest.java +++ b/core/tests/coretests/src/android/util/KeyValueListParserTest.java @@ -19,8 +19,9 @@ package android.util; import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/util/ListUtil.java b/core/tests/coretests/src/android/util/ListUtil.java index 2a7cb96bebfe..3748cf8e1405 100644 --- a/core/tests/coretests/src/android/util/ListUtil.java +++ b/core/tests/coretests/src/android/util/ListUtil.java @@ -20,13 +20,11 @@ import android.app.Instrumentation; import android.view.KeyEvent; import android.widget.ListView; - /** * Various useful stuff for instrumentation testing listview. */ public class ListUtil { - private final ListView mListView; private final Instrumentation mInstrumentation; diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/coretests/src/android/util/LocalLogTest.java index 5144c85e72a2..6cdcb5e542dc 100644 --- a/core/tests/coretests/src/android/util/LocalLogTest.java +++ b/core/tests/coretests/src/android/util/LocalLogTest.java @@ -16,7 +16,7 @@ package android.util; -import android.support.test.filters.LargeTest; +import androidx.test.filters.LargeTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/util/LogNullabilityTest.java b/core/tests/coretests/src/android/util/LogNullabilityTest.java index 3f4776d8b4fc..370885d3d4ac 100644 --- a/core/tests/coretests/src/android/util/LogNullabilityTest.java +++ b/core/tests/coretests/src/android/util/LogNullabilityTest.java @@ -19,8 +19,8 @@ package android.util; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java index 30c81b0c7228..d783c127d8c4 100644 --- a/core/tests/coretests/src/android/util/LogTest.java +++ b/core/tests/coretests/src/android/util/LogTest.java @@ -16,13 +16,12 @@ package android.util; -import junit.framework.Assert; -import junit.framework.TestCase; - import android.os.SystemProperties; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.Suppress; -import android.util.Log; + +import androidx.test.filters.Suppress; + +import junit.framework.TestCase; //This is an empty TestCase. @Suppress diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java index 3f03db9dc1fb..a1d48e689427 100644 --- a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java +++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java @@ -16,7 +16,7 @@ package android.util; -import android.support.test.filters.LargeTest; +import androidx.test.filters.LargeTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/util/LruCacheTest.java b/core/tests/coretests/src/android/util/LruCacheTest.java index 5a97158a85d1..1928bfdfc61d 100644 --- a/core/tests/coretests/src/android/util/LruCacheTest.java +++ b/core/tests/coretests/src/android/util/LruCacheTest.java @@ -16,12 +16,13 @@ package android.util; +import junit.framework.TestCase; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import junit.framework.TestCase; public final class LruCacheTest extends TestCase { private int expectedCreateCount; diff --git a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java index 55da665c7b34..30d5f778e945 100644 --- a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java +++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java @@ -16,12 +16,12 @@ package android.util; -import android.test.suitebuilder.annotation.SmallTest; - -import java.util.Calendar; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; +import java.util.Calendar; + /** * Unit tests for {@link MonthDisplayHelper}. */ diff --git a/core/tests/coretests/src/android/util/OrientationUtil.java b/core/tests/coretests/src/android/util/OrientationUtil.java index ecdca5d608c4..0361194dad2b 100644 --- a/core/tests/coretests/src/android/util/OrientationUtil.java +++ b/core/tests/coretests/src/android/util/OrientationUtil.java @@ -19,7 +19,6 @@ package android.util; import android.app.Activity; import android.app.Instrumentation; import android.content.pm.ActivityInfo; - import android.test.ActivityInstrumentationTestCase2; import com.android.internal.util.Preconditions; diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java index 360b8756f264..6cea2f3c92bd 100644 --- a/core/tests/coretests/src/android/util/PatternsTest.java +++ b/core/tests/coretests/src/android/util/PatternsTest.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.util; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; + +import junit.framework.TestCase; import java.util.regex.Matcher; import java.util.regex.Pattern; -import junit.framework.TestCase; - public class PatternsTest extends TestCase { // Tests for Patterns.TOP_LEVEL_DOMAIN diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java index 39d492d65415..caa12083fafb 100644 --- a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java +++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java @@ -16,7 +16,7 @@ package android.util; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/util/SparseLongArrayTest.java b/core/tests/coretests/src/android/util/SparseLongArrayTest.java index 5a5e893d1472..df2d752e04b9 100644 --- a/core/tests/coretests/src/android/util/SparseLongArrayTest.java +++ b/core/tests/coretests/src/android/util/SparseLongArrayTest.java @@ -19,9 +19,10 @@ package android.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import android.support.annotation.NonNull; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import android.annotation.NonNull; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java index e481ce04c82f..b5d62704ba29 100644 --- a/core/tests/coretests/src/android/util/StateSetTest.java +++ b/core/tests/coretests/src/android/util/StateSetTest.java @@ -16,8 +16,9 @@ package android.util; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; -import android.test.suitebuilder.annotation.SmallTest; /** * Tests for {@link StateSet} diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java index 03b4abd9b7a3..6e3ab796c5d3 100644 --- a/core/tests/coretests/src/android/util/TimestampedValueTest.java +++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java @@ -21,7 +21,8 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import android.os.Parcel; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java index 7bb4ab835e4f..77d05524d7cf 100644 --- a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java +++ b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java @@ -19,8 +19,9 @@ package android.util; import static org.junit.Assert.assertTrue; import android.os.Trace; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,7 +29,6 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; - /** * Tests for {@link TimingsTraceLog}. * <p>Usage: bit FrameworksCoreTests:android.util.TimingsTraceLogTest diff --git a/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java b/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java index ca12a153044d..fc082353fffb 100644 --- a/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java +++ b/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java @@ -16,12 +16,12 @@ package android.util; -import junit.framework.Assert; - import android.test.InstrumentationTestCase; import android.test.TouchUtils; import android.view.View; +import junit.framework.Assert; + /** * When entering touch mode via touch, the tests can be flaky. These asserts * are more flexible (allowing up to MAX_ATTEMPTS touches to enter touch mode via touch or diff --git a/core/tests/coretests/src/android/view/BigCache.java b/core/tests/coretests/src/android/view/BigCache.java index 21821763456a..6a1bcaaf2d00 100644 --- a/core/tests/coretests/src/android/view/BigCache.java +++ b/core/tests/coretests/src/android/view/BigCache.java @@ -16,16 +16,12 @@ package android.view; -import com.android.frameworks.coretests.R; - -import android.os.Bundle; import android.app.Activity; +import android.os.Bundle; import android.widget.LinearLayout; import android.widget.ScrollView; -import android.view.ViewGroup; -import android.view.View; -import android.view.Display; -import android.view.ViewConfiguration; + +import com.android.frameworks.coretests.R; /** * This activity contains two Views, one as big as the screen, one much larger. The large one diff --git a/core/tests/coretests/src/android/view/BigCacheTest.java b/core/tests/coretests/src/android/view/BigCacheTest.java index 8c2c865201aa..ed4b996b1d08 100644 --- a/core/tests/coretests/src/android/view/BigCacheTest.java +++ b/core/tests/coretests/src/android/view/BigCacheTest.java @@ -16,14 +16,12 @@ package android.view; -import android.view.BigCache; -import com.android.frameworks.coretests.R; - -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.View; -import android.view.ViewConfiguration; import android.graphics.Bitmap; +import android.test.ActivityInstrumentationTestCase; + +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; /** * Builds the drawing cache of two Views, one smaller than the maximum cache size, diff --git a/core/tests/coretests/src/android/view/BitmapDrawable.java b/core/tests/coretests/src/android/view/BitmapDrawable.java index f7bad84d6ddd..42c176f6b676 100644 --- a/core/tests/coretests/src/android/view/BitmapDrawable.java +++ b/core/tests/coretests/src/android/view/BitmapDrawable.java @@ -16,19 +16,10 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.Activity; -import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.AbsoluteLayout; -import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; + +import com.android.frameworks.coretests.R; public class BitmapDrawable extends Activity { @Override diff --git a/core/tests/coretests/src/android/view/CreateViewTest.java b/core/tests/coretests/src/android/view/CreateViewTest.java index 16656f688640..520f83f60836 100644 --- a/core/tests/coretests/src/android/view/CreateViewTest.java +++ b/core/tests/coretests/src/android/view/CreateViewTest.java @@ -16,16 +16,17 @@ package android.view; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + import android.content.Context; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.View; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.test.filters.SmallTest; + public class CreateViewTest extends AndroidTestCase implements PerformanceTestCase { public boolean isPerformanceOnly() { diff --git a/core/tests/coretests/src/android/view/Disabled.java b/core/tests/coretests/src/android/view/Disabled.java index d3c7470bd0f9..ce90039f360f 100644 --- a/core/tests/coretests/src/android/view/Disabled.java +++ b/core/tests/coretests/src/android/view/Disabled.java @@ -16,13 +16,12 @@ package android.view; -import com.android.frameworks.coretests.R; - +import android.app.Activity; import android.os.Bundle; -import android.widget.Button; -import android.view.View; import android.view.View.OnClickListener; -import android.app.Activity; +import android.widget.Button; + +import com.android.frameworks.coretests.R; /** * Exercise View's disabled state. diff --git a/core/tests/coretests/src/android/view/DisabledLongpressTest.java b/core/tests/coretests/src/android/view/DisabledLongpressTest.java index faa0865c5a1c..a9fec01c8b34 100644 --- a/core/tests/coretests/src/android/view/DisabledLongpressTest.java +++ b/core/tests/coretests/src/android/view/DisabledLongpressTest.java @@ -18,11 +18,12 @@ package android.view; import android.test.ActivityInstrumentationTestCase; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.util.KeyUtils; import android.view.View.OnLongClickListener; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/view/DisabledTest.java b/core/tests/coretests/src/android/view/DisabledTest.java index 291a11c289b7..8922e5fda206 100644 --- a/core/tests/coretests/src/android/view/DisabledTest.java +++ b/core/tests/coretests/src/android/view/DisabledTest.java @@ -18,12 +18,11 @@ package android.view; import android.test.ActivityInstrumentationTestCase; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.KeyEvent; -import android.view.View; import android.widget.Button; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index 8e4f2cd2fbff..dd50af877bbb 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -33,17 +33,17 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Parcel; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.DisplayCutout.ParcelableWrapper; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Collections; - @RunWith(AndroidJUnit4.class) @SmallTest @Presubmit diff --git a/core/tests/coretests/src/android/view/DrawableBgMinSize.java b/core/tests/coretests/src/android/view/DrawableBgMinSize.java index 58bfb8a77064..ba9ba008b2ef 100644 --- a/core/tests/coretests/src/android/view/DrawableBgMinSize.java +++ b/core/tests/coretests/src/android/view/DrawableBgMinSize.java @@ -16,12 +16,9 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.view.View; import android.view.View.OnClickListener; import android.widget.AbsoluteLayout; import android.widget.Button; @@ -30,6 +27,8 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.frameworks.coretests.R; + /** * Views should obey their background {@link Drawable}'s minimum size * requirements ({@link Drawable#getMinimumHeight()} and diff --git a/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java b/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java index e705c87a6675..1735e8cb037f 100644 --- a/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java +++ b/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java @@ -16,14 +16,9 @@ package android.view; -import com.android.frameworks.coretests.R; -import android.view.DrawableBgMinSize; -import android.test.TouchUtils; -import android.test.suitebuilder.annotation.MediumTest; - import android.graphics.drawable.Drawable; import android.test.ActivityInstrumentationTestCase; -import android.view.View; +import android.test.TouchUtils; import android.widget.AbsoluteLayout; import android.widget.Button; import android.widget.FrameLayout; @@ -31,6 +26,10 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * {@link DrawableBgMinSize} exercises Views to obey their background drawable's * minimum sizes. diff --git a/core/tests/coretests/src/android/view/FocusFinderTest.java b/core/tests/coretests/src/android/view/FocusFinderTest.java index 2732a04b44d5..b35c64a09ade 100644 --- a/core/tests/coretests/src/android/view/FocusFinderTest.java +++ b/core/tests/coretests/src/android/view/FocusFinderTest.java @@ -18,7 +18,8 @@ package android.view; import android.graphics.Rect; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; public class FocusFinderTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/view/GlobalFocusChange.java b/core/tests/coretests/src/android/view/GlobalFocusChange.java index 041c0ded366f..ee92b64f3947 100644 --- a/core/tests/coretests/src/android/view/GlobalFocusChange.java +++ b/core/tests/coretests/src/android/view/GlobalFocusChange.java @@ -16,12 +16,10 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.ViewTreeObserver; -import android.view.View; + +import com.android.frameworks.coretests.R; public class GlobalFocusChange extends Activity implements ViewTreeObserver.OnGlobalFocusChangeListener { public View mOldFocus; diff --git a/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java b/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java index dab7b90a447d..960654a926ce 100644 --- a/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java +++ b/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java @@ -17,13 +17,13 @@ package android.view; import android.test.ActivityInstrumentationTestCase; -import android.test.FlakyTest; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.Suppress; -import android.view.View; -import android.view.KeyEvent; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + import com.android.frameworks.coretests.R; @Suppress // Flaky @@ -50,7 +50,7 @@ public class GlobalFocusChangeTest extends ActivityInstrumentationTestCase<Globa super.tearDown(); } - @FlakyTest(tolerance = 4) + @FlakyTest @LargeTest public void testFocusChange() throws Exception { sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); @@ -62,7 +62,7 @@ public class GlobalFocusChangeTest extends ActivityInstrumentationTestCase<Globa assertSame(mRight, mActivity.mNewFocus); } - @FlakyTest(tolerance = 4) + @FlakyTest @MediumTest public void testEnterTouchMode() throws Exception { assertTrue(mLeft.isFocused()); @@ -73,7 +73,7 @@ public class GlobalFocusChangeTest extends ActivityInstrumentationTestCase<Globa assertSame(null, mActivity.mNewFocus); } - @FlakyTest(tolerance = 4) + @FlakyTest @MediumTest public void testLeaveTouchMode() throws Exception { assertTrue(mLeft.isFocused()); diff --git a/core/tests/coretests/src/android/view/HandlerActionQueueTest.java b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java index fd8f23abc45c..bedb75bb7472 100644 --- a/core/tests/coretests/src/android/view/HandlerActionQueueTest.java +++ b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java @@ -17,7 +17,8 @@ package android.view; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; public class HandlerActionQueueTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/view/Include.java b/core/tests/coretests/src/android/view/Include.java index e90c48471c83..4b6aefb112e6 100644 --- a/core/tests/coretests/src/android/view/Include.java +++ b/core/tests/coretests/src/android/view/Include.java @@ -16,10 +16,10 @@ package android.view; -import com.android.frameworks.coretests.R; - -import android.os.Bundle; import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; /** * Exercise <include /> tag in XML files. diff --git a/core/tests/coretests/src/android/view/IncludeTest.java b/core/tests/coretests/src/android/view/IncludeTest.java index cdcfa3ce5478..e4905597d16a 100644 --- a/core/tests/coretests/src/android/view/IncludeTest.java +++ b/core/tests/coretests/src/android/view/IncludeTest.java @@ -16,13 +16,11 @@ package android.view; -import android.view.Include; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.View; -import android.view.ViewGroup; + +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; public class IncludeTest extends ActivityInstrumentationTestCase<Include> { public IncludeTest() { diff --git a/core/tests/coretests/src/android/view/InflateTest.java b/core/tests/coretests/src/android/view/InflateTest.java index cb4f8e2399b1..939d6eaba3c9 100644 --- a/core/tests/coretests/src/android/view/InflateTest.java +++ b/core/tests/coretests/src/android/view/InflateTest.java @@ -20,13 +20,11 @@ import android.content.Context; import android.content.res.Resources; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; -import android.test.suitebuilder.annotation.SmallTest; import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import com.android.frameworks.coretests.R; -import java.util.Map; +import androidx.test.filters.SmallTest; + +import com.android.frameworks.coretests.R; public class InflateTest extends AndroidTestCase implements PerformanceTestCase { private LayoutInflater mInflater; diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 81ca9109c643..7f00ad9ed2f2 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -18,7 +18,9 @@ package android.view; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; + import static junit.framework.Assert.assertEquals; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -27,12 +29,13 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.FlakyTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; import android.view.SurfaceControl.Transaction; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index d3d274a64682..d44745121a22 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -17,18 +17,20 @@ package android.view; import static android.view.InsetsState.TYPE_TOP_BAR; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; + import static org.mockito.Mockito.mock; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.FlakyTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; @Presubmit @FlakyTest(detail = "Promote once confirmed non-flaky") diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 6d0f98433168..82cd2131ab4e 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -17,6 +17,7 @@ package android.view; import static android.view.InsetsState.TYPE_TOP_BAR; + import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.reset; @@ -24,10 +25,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.FlakyTest; -import android.support.test.runner.AndroidJUnit4; import android.view.SurfaceControl.Transaction; +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index ed472d2a7f64..98ab3e785d0a 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -17,13 +17,15 @@ package android.view; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; + import static junit.framework.Assert.assertEquals; import android.graphics.Insets; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.FlakyTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 95af525c3746..9857b7a1e035 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -24,20 +24,23 @@ import static android.view.InsetsState.TYPE_SIDE_BAR_1; 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 junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.junit.Assert.assertNotEquals; import android.graphics.Insets; import android.graphics.Rect; import android.os.Parcel; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.FlakyTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseIntArray; import android.view.WindowInsets.Type; +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java index b9d95e542f11..88d3bdbe3c92 100644 --- a/core/tests/coretests/src/android/view/KeyEventTest.java +++ b/core/tests/coretests/src/android/view/KeyEventTest.java @@ -20,8 +20,8 @@ import static android.view.Display.INVALID_DISPLAY; import static org.junit.Assert.assertEquals; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/ListContextMenu.java b/core/tests/coretests/src/android/view/ListContextMenu.java index 1b4ece6b8eef..e333b9638d25 100644 --- a/core/tests/coretests/src/android/view/ListContextMenu.java +++ b/core/tests/coretests/src/android/view/ListContextMenu.java @@ -16,24 +16,17 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.ListActivity; import android.content.Context; import android.os.Bundle; import android.util.Log; -import android.view.ContextMenu; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.view.ViewGroup; -import android.view.LayoutInflater; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.TextView; +import com.android.frameworks.coretests.R; + /** * Exercises context menus in lists */ diff --git a/core/tests/coretests/src/android/view/Longpress.java b/core/tests/coretests/src/android/view/Longpress.java index e8e6f13e30e9..86fa9e54afd3 100644 --- a/core/tests/coretests/src/android/view/Longpress.java +++ b/core/tests/coretests/src/android/view/Longpress.java @@ -16,11 +16,11 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + public class Longpress extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/core/tests/coretests/src/android/view/LongpressTest.java b/core/tests/coretests/src/android/view/LongpressTest.java index d3d7589ae641..4dfecd0b2055 100644 --- a/core/tests/coretests/src/android/view/LongpressTest.java +++ b/core/tests/coretests/src/android/view/LongpressTest.java @@ -18,11 +18,12 @@ package android.view; import android.test.ActivityInstrumentationTestCase; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.util.KeyUtils; import android.view.View.OnLongClickListener; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/view/MenuTest.java b/core/tests/coretests/src/android/view/MenuTest.java index 116b38a6551b..794769cc032f 100644 --- a/core/tests/coretests/src/android/view/MenuTest.java +++ b/core/tests/coretests/src/android/view/MenuTest.java @@ -17,7 +17,8 @@ package android.view; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import com.android.frameworks.coretests.R; import com.android.internal.view.menu.MenuBuilder; diff --git a/core/tests/coretests/src/android/view/Merge.java b/core/tests/coretests/src/android/view/Merge.java index bdacd819d2e8..8020e4b07815 100644 --- a/core/tests/coretests/src/android/view/Merge.java +++ b/core/tests/coretests/src/android/view/Merge.java @@ -16,13 +16,11 @@ package android.view; -import com.android.frameworks.coretests.R; - -import android.os.Bundle; import android.app.Activity; +import android.os.Bundle; import android.widget.LinearLayout; -import android.view.ViewGroup; -import android.view.LayoutInflater; + +import com.android.frameworks.coretests.R; /** * Exercise <merge /> tag in XML files. diff --git a/core/tests/coretests/src/android/view/MergeTest.java b/core/tests/coretests/src/android/view/MergeTest.java index acfee7eca036..eccc9cb23e2a 100644 --- a/core/tests/coretests/src/android/view/MergeTest.java +++ b/core/tests/coretests/src/android/view/MergeTest.java @@ -16,11 +16,9 @@ package android.view; -import android.view.Merge; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.ViewGroup; + +import androidx.test.filters.MediumTest; public class MergeTest extends ActivityInstrumentationTestCase<Merge> { public MergeTest() { diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java index 023526f7e37f..cadf37e0c53c 100644 --- a/core/tests/coretests/src/android/view/MotionEventTest.java +++ b/core/tests/coretests/src/android/view/MotionEventTest.java @@ -22,11 +22,12 @@ import static android.view.MotionEvent.TOOL_TYPE_FINGER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/MutateDrawable.java b/core/tests/coretests/src/android/view/MutateDrawable.java index 39b57894696f..1ec525d6fe9e 100644 --- a/core/tests/coretests/src/android/view/MutateDrawable.java +++ b/core/tests/coretests/src/android/view/MutateDrawable.java @@ -18,8 +18,9 @@ package android.view; import android.app.Activity; import android.os.Bundle; -import android.widget.LinearLayout; import android.widget.Button; +import android.widget.LinearLayout; + import com.android.frameworks.coretests.R; public class MutateDrawable extends Activity { diff --git a/core/tests/coretests/src/android/view/MutateDrawableTest.java b/core/tests/coretests/src/android/view/MutateDrawableTest.java index 74e011d28508..d63ffb17208f 100644 --- a/core/tests/coretests/src/android/view/MutateDrawableTest.java +++ b/core/tests/coretests/src/android/view/MutateDrawableTest.java @@ -17,9 +17,8 @@ package android.view; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.View; -import android.view.MutateDrawable; + +import androidx.test.filters.MediumTest; public class MutateDrawableTest extends ActivityInstrumentationTestCase2<MutateDrawable> { private View mFirstButton; diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java index 97fe980e01dc..bec9b557a40d 100644 --- a/core/tests/coretests/src/android/view/PinchZoomAction.java +++ b/core/tests/coretests/src/android/view/PinchZoomAction.java @@ -30,8 +30,6 @@ import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.support.test.espresso.action.Swiper; import android.support.test.espresso.util.HumanReadables; -import android.view.MotionEvent; -import android.view.View; import org.hamcrest.Matcher; diff --git a/core/tests/coretests/src/android/view/PopupWindowVisibility.java b/core/tests/coretests/src/android/view/PopupWindowVisibility.java index 85ce04fae71b..e6430d3cea32 100644 --- a/core/tests/coretests/src/android/view/PopupWindowVisibility.java +++ b/core/tests/coretests/src/android/view/PopupWindowVisibility.java @@ -18,7 +18,6 @@ package android.view; import android.app.Activity; import android.os.Bundle; -import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; diff --git a/core/tests/coretests/src/android/view/PreDrawListener.java b/core/tests/coretests/src/android/view/PreDrawListener.java index 60bbee483173..77bd36c9001a 100644 --- a/core/tests/coretests/src/android/view/PreDrawListener.java +++ b/core/tests/coretests/src/android/view/PreDrawListener.java @@ -20,15 +20,12 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.AttributeSet; -import android.view.View; -import android.view.ViewTreeObserver; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import com.android.frameworks.coretests.R; - /** * Tests views with popupWindows becoming invisible */ diff --git a/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java b/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java index b52d98c906c4..786c22bf04f6 100644 --- a/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java +++ b/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java @@ -21,10 +21,11 @@ import static org.junit.Assert.assertNull; import android.app.Activity; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; + +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; diff --git a/core/tests/coretests/src/android/view/RunQueue.java b/core/tests/coretests/src/android/view/RunQueue.java index 85dd32e720bd..75444339a1aa 100644 --- a/core/tests/coretests/src/android/view/RunQueue.java +++ b/core/tests/coretests/src/android/view/RunQueue.java @@ -19,8 +19,7 @@ package android.view; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; -import android.view.View; -import android.view.ViewTreeObserver; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/view/RunQueueTest.java b/core/tests/coretests/src/android/view/RunQueueTest.java index d69860beace4..f42f5902a5c3 100644 --- a/core/tests/coretests/src/android/view/RunQueueTest.java +++ b/core/tests/coretests/src/android/view/RunQueueTest.java @@ -17,7 +17,8 @@ package android.view; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.MediumTest; public class RunQueueTest extends ActivityInstrumentationTestCase<RunQueue> { public RunQueueTest() { diff --git a/core/tests/coretests/src/android/view/ScaleGesture.java b/core/tests/coretests/src/android/view/ScaleGesture.java index a954a4a497e2..235b2244e3f8 100644 --- a/core/tests/coretests/src/android/view/ScaleGesture.java +++ b/core/tests/coretests/src/android/view/ScaleGesture.java @@ -16,16 +16,11 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.util.Log; -import android.util.TypedValue; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener; -import android.widget.TextView; + +import com.android.frameworks.coretests.R; public class ScaleGesture extends Activity { private ScaleGestureDetector mScaleGestureDetector; diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java index fba8eae6c3dd..199013513bb3 100644 --- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java +++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java @@ -16,26 +16,22 @@ package android.view; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.matcher.ViewMatchers.withId; + import android.content.Context; -import android.support.test.filters.LargeTest; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; import android.test.ActivityInstrumentationTestCase2; import android.util.DisplayMetrics; -import android.view.PinchZoomAction; -import android.view.ScaleGesture; -import android.view.WindowManager; import android.widget.TextView; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; + import com.android.frameworks.coretests.R; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; - -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.Espresso.onView; @LargeTest public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> { diff --git a/core/tests/coretests/src/android/view/SetTagsTest.java b/core/tests/coretests/src/android/view/SetTagsTest.java index 373dce68d495..16997139d83f 100644 --- a/core/tests/coretests/src/android/view/SetTagsTest.java +++ b/core/tests/coretests/src/android/view/SetTagsTest.java @@ -16,12 +16,13 @@ package android.view; -import com.android.frameworks.coretests.R; -import android.test.suitebuilder.annotation.MediumTest; - import android.test.ActivityInstrumentationTestCase2; import android.widget.Button; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * Exercises {@link android.view.View}'s tags property. */ diff --git a/core/tests/coretests/src/android/view/StubbedView.java b/core/tests/coretests/src/android/view/StubbedView.java index 612095c4e43d..c96b9b704a54 100644 --- a/core/tests/coretests/src/android/view/StubbedView.java +++ b/core/tests/coretests/src/android/view/StubbedView.java @@ -16,11 +16,10 @@ package android.view; -import com.android.frameworks.coretests.R; - -import android.os.Bundle; import android.app.Activity; -import android.view.View; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; /** * Exercise <ViewStub /> tag in XML files. diff --git a/core/tests/coretests/src/android/view/VelocityTest.java b/core/tests/coretests/src/android/view/VelocityTest.java index 7f32208659be..c116f4dc8a43 100644 --- a/core/tests/coretests/src/android/view/VelocityTest.java +++ b/core/tests/coretests/src/android/view/VelocityTest.java @@ -16,16 +16,17 @@ package android.view; -import android.test.suitebuilder.annotation.Suppress; -import junit.framework.Assert; - import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + +import junit.framework.Assert; + /** * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br> * To launch this test, use :<br> diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java index aa8f8d82e725..1a8dd9909b03 100644 --- a/core/tests/coretests/src/android/view/ViewAttachTest.java +++ b/core/tests/coretests/src/android/view/ViewAttachTest.java @@ -16,16 +16,13 @@ package android.view; -import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; import android.os.SystemClock; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.LargeTest; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/view/ViewAttachTestActivity.java b/core/tests/coretests/src/android/view/ViewAttachTestActivity.java index 59e25aee7dc5..bcbc8134c226 100644 --- a/core/tests/coretests/src/android/view/ViewAttachTestActivity.java +++ b/core/tests/coretests/src/android/view/ViewAttachTestActivity.java @@ -16,11 +16,11 @@ package android.view; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + public class ViewAttachTestActivity extends Activity { public static final String TAG = "OnAttachedTest"; @Override diff --git a/core/tests/coretests/src/android/view/ViewAttachView.java b/core/tests/coretests/src/android/view/ViewAttachView.java index 5af2d8f33cab..2f3ff8f8769e 100644 --- a/core/tests/coretests/src/android/view/ViewAttachView.java +++ b/core/tests/coretests/src/android/view/ViewAttachView.java @@ -22,7 +22,6 @@ import android.graphics.Color; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; -import android.view.View; /** * A View that will throw a RuntimeException if onAttachedToWindow and diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java index 4405934e62e5..218047c5f45e 100644 --- a/core/tests/coretests/src/android/view/ViewCaptureTest.java +++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java @@ -21,14 +21,15 @@ import static org.junit.Assert.assertTrue; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.support.test.filters.SmallTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseIntArray; import android.view.ViewDebug.CanvasProvider; import android.view.ViewDebug.HardwareCanvasProvider; import android.view.ViewDebug.SoftwareCanvasProvider; +import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.R; import org.junit.Assert; diff --git a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java index 20e3eb585435..2f51c2459af9 100644 --- a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java +++ b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.Nullable; import android.app.Activity; import android.os.Bundle; + import com.android.frameworks.coretests.R; public class ViewCaptureTestActivity extends Activity { diff --git a/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java index b4ef0e7e76bd..c24cd35ff256 100644 --- a/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java @@ -18,7 +18,8 @@ package android.view; import android.content.Context; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; public class ViewGroupAttributesTest extends AndroidTestCase { diff --git a/core/tests/coretests/src/android/view/ViewGroupChildren.java b/core/tests/coretests/src/android/view/ViewGroupChildren.java index f39720b269b3..d5d98793499e 100644 --- a/core/tests/coretests/src/android/view/ViewGroupChildren.java +++ b/core/tests/coretests/src/android/view/ViewGroupChildren.java @@ -16,12 +16,10 @@ package android.view; -import com.android.frameworks.coretests.R; - -import android.os.Bundle; -import android.widget.Button; -import android.view.View; import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; /** * Exercise ViewGroup's ability to add and remove children. diff --git a/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java b/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java index d1665ef8cf14..07cb2a144f32 100644 --- a/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java @@ -16,18 +16,16 @@ package android.view; -import com.android.frameworks.coretests.R; -import android.view.ViewGroupChildren; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; -import android.test.UiThreadTest; -import android.view.View; -import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * Exercises {@link android.view.ViewGroup}'s ability to add/remove children. */ diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java index 93ad41f08b06..54524b2c8e70 100644 --- a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java @@ -22,12 +22,13 @@ import static junit.framework.Assert.fail; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.widget.FrameLayout; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/ViewInvalidateTest.java b/core/tests/coretests/src/android/view/ViewInvalidateTest.java index 115504b50adf..c25a2deb2a05 100644 --- a/core/tests/coretests/src/android/view/ViewInvalidateTest.java +++ b/core/tests/coretests/src/android/view/ViewInvalidateTest.java @@ -21,24 +21,30 @@ import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Activity; import android.content.Context; import android.graphics.Rect; -import android.support.test.InstrumentationRegistry; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.LargeTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; +import android.view.ViewTreeObserver.OnDrawListener; import android.widget.FrameLayout; -import com.android.compatibility.common.util.WidgetTestUtils; +import androidx.test.InstrumentationRegistry; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Test of invalidates, drawing, and the flags that support them */ @@ -281,4 +287,42 @@ public class ViewInvalidateTest { View.PFLAG_DRAWN); assertTrue(getViewRoot(mParent).mIsAnimating); } + + /** Copied from cts/common/device-side/util. */ + static class WidgetTestUtils { + public static void runOnMainAndDrawSync(@NonNull final ActivityTestRule activityTestRule, + @NonNull final View view, @Nullable final Runnable runner) throws Throwable { + final CountDownLatch latch = new CountDownLatch(1); + + activityTestRule.runOnUiThread(() -> { + final OnDrawListener listener = new OnDrawListener() { + @Override + public void onDraw() { + // posting so that the sync happens after the draw that's about to happen + view.post(() -> { + activityTestRule.getActivity().getWindow().getDecorView() + .getViewTreeObserver().removeOnDrawListener(this); + latch.countDown(); + }); + } + }; + + activityTestRule.getActivity().getWindow().getDecorView() + .getViewTreeObserver().addOnDrawListener(listener); + + if (runner != null) { + runner.run(); + } + view.invalidate(); + }); + + try { + Assert.assertTrue("Expected draw pass occurred within 5 seconds", + latch.await(5, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index ca6d6cfedb76..9a5784774d1c 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -23,9 +23,10 @@ import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/ViewStubTest.java b/core/tests/coretests/src/android/view/ViewStubTest.java index ebd52a6d7774..504a4eca29a2 100644 --- a/core/tests/coretests/src/android/view/ViewStubTest.java +++ b/core/tests/coretests/src/android/view/ViewStubTest.java @@ -16,14 +16,12 @@ package android.view; -import android.view.StubbedView; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.UiThreadTest; -import android.view.View; -import android.view.ViewStub; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; public class ViewStubTest extends ActivityInstrumentationTestCase<StubbedView> { public ViewStubTest() { diff --git a/core/tests/coretests/src/android/view/ViewTransientStateTest.java b/core/tests/coretests/src/android/view/ViewTransientStateTest.java index 36ea01deea22..3f73b0712d55 100644 --- a/core/tests/coretests/src/android/view/ViewTransientStateTest.java +++ b/core/tests/coretests/src/android/view/ViewTransientStateTest.java @@ -18,12 +18,11 @@ package android.view; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.MediumTest; -import com.android.frameworks.coretests.R; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; -import static org.junit.Assert.assertFalse; +import com.android.frameworks.coretests.R; /** * Exercise set View's transient state diff --git a/core/tests/coretests/src/android/view/Visibility.java b/core/tests/coretests/src/android/view/Visibility.java index 031568c5a446..8e3de4e4a6a3 100644 --- a/core/tests/coretests/src/android/view/Visibility.java +++ b/core/tests/coretests/src/android/view/Visibility.java @@ -16,12 +16,11 @@ package android.view; -import com.android.frameworks.coretests.R; - +import android.app.Activity; import android.os.Bundle; import android.widget.Button; -import android.view.View; -import android.app.Activity; + +import com.android.frameworks.coretests.R; /** * Exercise View's ability to change their visibility: GONE, INVISIBLE and diff --git a/core/tests/coretests/src/android/view/VisibilityCallback.java b/core/tests/coretests/src/android/view/VisibilityCallback.java index f98a0a882764..c9659737e479 100644 --- a/core/tests/coretests/src/android/view/VisibilityCallback.java +++ b/core/tests/coretests/src/android/view/VisibilityCallback.java @@ -16,16 +16,15 @@ package android.view; +import android.app.Activity; import android.content.Context; +import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; +import android.widget.Button; import android.widget.TextView; -import com.android.frameworks.coretests.R; -import android.os.Bundle; -import android.widget.Button; -import android.view.View; -import android.app.Activity; +import com.android.frameworks.coretests.R; /** * Exercise View's ability to change their visibility: GONE, INVISIBLE and diff --git a/core/tests/coretests/src/android/view/VisibilityCallbackTest.java b/core/tests/coretests/src/android/view/VisibilityCallbackTest.java index ec956d24f29c..d7c7b4c0094f 100644 --- a/core/tests/coretests/src/android/view/VisibilityCallbackTest.java +++ b/core/tests/coretests/src/android/view/VisibilityCallbackTest.java @@ -17,12 +17,12 @@ package android.view; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.View; -import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/view/VisibilityTest.java b/core/tests/coretests/src/android/view/VisibilityTest.java index 29c1c8a428e7..83a77024cc43 100644 --- a/core/tests/coretests/src/android/view/VisibilityTest.java +++ b/core/tests/coretests/src/android/view/VisibilityTest.java @@ -20,11 +20,12 @@ import static android.view.KeyEvent.KEYCODE_DPAD_CENTER; import static android.view.KeyEvent.KEYCODE_DPAD_LEFT; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.Button; import android.widget.TextView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java index 1513a1a7d222..15a96a144dd5 100644 --- a/core/tests/coretests/src/android/view/WindowInsetsTest.java +++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java @@ -17,18 +17,18 @@ package android.view; import static android.view.WindowInsets.Type.ime; -import static android.view.WindowInsets.Type.indexOf; import static android.view.WindowInsets.Type.sideBars; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.graphics.Insets; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.WindowInsets.Builder; -import android.view.WindowInsets.Type; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/ZeroSized.java b/core/tests/coretests/src/android/view/ZeroSized.java index f2a6b3ef3f6d..9e2dfcb8e3d4 100644 --- a/core/tests/coretests/src/android/view/ZeroSized.java +++ b/core/tests/coretests/src/android/view/ZeroSized.java @@ -16,10 +16,10 @@ package android.view; -import com.android.frameworks.coretests.R; - -import android.os.Bundle; import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; /** * This activity contains Views with various widths and heights. The goal is to exercise the diff --git a/core/tests/coretests/src/android/view/ZeroSizedTest.java b/core/tests/coretests/src/android/view/ZeroSizedTest.java index 193fc9842e8d..946bf6866d26 100644 --- a/core/tests/coretests/src/android/view/ZeroSizedTest.java +++ b/core/tests/coretests/src/android/view/ZeroSizedTest.java @@ -16,13 +16,12 @@ package android.view; -import android.view.ZeroSized; -import com.android.frameworks.coretests.R; - -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.View; import android.graphics.Bitmap; +import android.test.ActivityInstrumentationTestCase; + +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; /** * Builds the drawing cache of Views of various dimension. The assumption is that diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java index 7f675ffffee5..a88968bfd089 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java @@ -28,10 +28,11 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; import android.view.View; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java index 3e03414367bd..46c96c94dc11 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java @@ -21,7 +21,8 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import android.os.Parcel; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java index 318d122b3f2f..ab24f89015c7 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java @@ -22,10 +22,12 @@ import static org.mockito.MockitoAnnotations.initMocks; import android.os.Bundle; import android.os.RemoteException; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; import libcore.util.EmptyArray; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index 506e5447a239..0ed690ceeef4 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -25,11 +25,12 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import android.os.Parcel; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; import android.util.ArraySet; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.util.CollectionUtils; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java index 4814c61abe7c..11f4e3cf9304 100644 --- a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java +++ b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java @@ -14,7 +14,7 @@ package android.view.accessibility; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java index 7619af23375f..33bc59349230 100644 --- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java +++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.view.autofill; import static com.google.common.truth.Truth.assertThat; diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 73cceaeee547..6ecb63abf586 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -13,6 +13,7 @@ * 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; @@ -114,7 +115,7 @@ public class ContentCaptureSessionTest { } @Override - void flush() { + void flush(int reason) { throw new UnsupportedOperationException("should not have been called"); } diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java index bbfe01c379f1..099f41d10e27 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java @@ -13,6 +13,7 @@ * 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; @@ -24,7 +25,6 @@ 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; @@ -32,6 +32,8 @@ 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; diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java index 0fd0136a85ed..ace6611a52c5 100644 --- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java @@ -28,11 +28,12 @@ import static org.junit.Assert.assertTrue; import android.graphics.Matrix; import android.graphics.RectF; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.view.inputmethod.CursorAnchorInfo.Builder; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java index 1b00e0939688..f24e232765c4 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java @@ -26,9 +26,10 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Bundle; import android.os.Parcel; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java index 55e5e3698f33..9f259a841319 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java @@ -20,11 +20,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.WindowManager; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index 8df18480ed5a..e2fb46af5b64 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -19,10 +19,11 @@ package android.view.inputmethod; import static org.junit.Assert.assertEquals; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java index c76359ee39bc..1e0e1235161c 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java @@ -23,10 +23,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java index 8c96b588d08f..453ad72b64dc 100644 --- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java @@ -23,10 +23,11 @@ import static org.junit.Assert.assertTrue; import android.graphics.RectF; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java index 657a7fc7b798..ba85d765ceda 100644 --- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java +++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java @@ -18,7 +18,6 @@ package android.view.menu; import android.content.Context; import android.graphics.Point; -import android.support.test.filters.MediumTest; import android.test.ActivityInstrumentationTestCase; import android.util.PollingCheck; import android.view.Display; @@ -26,6 +25,8 @@ import android.view.View; import android.view.WindowManager; import android.widget.espresso.ContextMenuUtils; +import androidx.test.filters.MediumTest; + @MediumTest public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenuActivity> { diff --git a/core/tests/coretests/src/android/view/menu/MenuLayout.java b/core/tests/coretests/src/android/view/menu/MenuLayout.java index 356c9482de41..33ee5158bc12 100644 --- a/core/tests/coretests/src/android/view/menu/MenuLayout.java +++ b/core/tests/coretests/src/android/view/menu/MenuLayout.java @@ -16,8 +16,6 @@ package android.view.menu; -import android.view.menu.MenuScenario.Params; - import android.os.Bundle; import android.view.Menu; import android.widget.Button; diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java index 8ed0d86d850d..ff9f166afb5f 100644 --- a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java +++ b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java @@ -16,13 +16,10 @@ package android.view.menu; +import android.test.ActivityInstrumentationTestCase; import android.util.KeyUtils; -import com.android.internal.view.menu.IconMenuView; -import com.android.internal.view.menu.MenuBuilder; -import android.content.pm.ActivityInfo; -import android.support.test.filters.LargeTest; -import android.test.ActivityInstrumentationTestCase; +import androidx.test.filters.LargeTest; @LargeTest public class MenuLayoutLandscapeTest extends ActivityInstrumentationTestCase<MenuLayoutLandscape> { diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java index ccf12643593b..360be5343a2b 100644 --- a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java +++ b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java @@ -16,11 +16,11 @@ package android.view.menu; -import android.content.pm.ActivityInfo; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase; import android.util.KeyUtils; +import androidx.test.filters.LargeTest; + @LargeTest public class MenuLayoutPortraitTest extends ActivityInstrumentationTestCase<MenuLayoutPortrait> { private static final String LONG_TITLE = "Really really really really really really really really really really long title"; diff --git a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java index 82ad858ba3a9..c18e36136990 100644 --- a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java +++ b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java @@ -16,17 +16,12 @@ package android.view.menu; -import android.view.menu.MenuWith1Item; -import android.util.KeyUtils; -import com.android.internal.view.menu.MenuBuilder; - -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.TouchUtils; - import android.test.ActivityInstrumentationTestCase; +import android.util.KeyUtils; import android.view.KeyEvent; -import android.view.View; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; public class MenuWith1ItemTest extends ActivityInstrumentationTestCase<MenuWith1Item> { private MenuWith1Item mActivity; diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java index 4a6c093e3bd1..780e15ab885e 100644 --- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java @@ -22,8 +22,9 @@ import static android.view.textclassifier.ConversationActions.Message.PERSON_USE import static com.google.common.truth.Truth.assertThat; import android.app.Person; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.google.android.textclassifier.ActionsSuggestionsModel; diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java index 018085698d0c..fef6583f4f98 100644 --- a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java +++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; @@ -29,9 +30,8 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.support.test.InstrumentationRegistry; -import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; import com.google.common.base.Preconditions; diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java index aaadefb90ece..3fc8e4c2eecd 100644 --- a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.view.textclassifier; import static com.google.common.truth.Truth.assertThat; import android.content.Intent; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.google.android.textclassifier.AnnotatorModel; diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java index 88d162b34144..74b8e3b49f7b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.view.textclassifier; import static com.google.common.truth.Truth.assertThat; @@ -20,9 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; import android.os.LocaleList; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java index a3c6179792b1..46e3a4c26dde 100644 --- a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java @@ -19,8 +19,9 @@ package android.view.textclassifier; import static org.junit.Assert.assertEquals; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java index 54007fb98e67..9662182b8cf8 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java @@ -20,8 +20,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index a3f69d93ad44..4fcd51c9be1d 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -26,9 +26,10 @@ import android.content.Context; import android.content.Intent; import android.os.LocaleList; import android.service.textclassifier.TextClassifierService; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java index aaf7312fe0e6..99c959ef57e4 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java @@ -32,11 +32,12 @@ import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.View; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index ec6101c09b7b..7009fb2ea758 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -24,11 +24,12 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.Intent; import android.os.LocaleList; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.text.Spannable; import android.text.SpannableString; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java index 1dcaed6f6a1e..d0d32e3a507a 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java @@ -21,8 +21,9 @@ import static org.junit.Assert.assertEquals; import android.icu.util.ULocale; import android.os.Bundle; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java index f022d040246b..b9cc8f4ba4a0 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java @@ -21,10 +21,11 @@ import static org.junit.Assert.assertEquals; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java index 2ea49f7d21be..30cc4e8990e0 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java @@ -21,8 +21,9 @@ import static org.junit.Assert.assertEquals; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java index 8e4f02c668b7..5e8e5823eefa 100644 --- a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java @@ -23,13 +23,14 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import android.metrics.LogMaker; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; import android.view.textclassifier.GenerateLinksLogger; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextLinks; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 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 0597a89e87d4..b1b74160ecd5 100644 --- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.view.textclassifier.logging; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE; @@ -24,13 +25,14 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ import static com.google.common.truth.Truth.assertThat; import android.metrics.LogMaker; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.textclassifier.ConversationAction; import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassifierEvent; import android.view.textclassifier.TextClassifierEventTronLogger; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.logging.MetricsLogger; import org.junit.Before; diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java index 4a1c414234bd..638d894b114a 100644 --- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java +++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java @@ -16,15 +16,16 @@ package android.view.textservice; +import static android.test.MoreAsserts.assertNotEqual; + import android.os.Parcel; import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; import java.util.Arrays; import java.util.Locale; -import static android.test.MoreAsserts.assertNotEqual; - /** * TODO: Most of part can be, and probably should be, moved to CTS. */ diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java index 4f31c4ee72a4..6edc1628b163 100644 --- a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java +++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java @@ -24,18 +24,14 @@ import static org.junit.Assert.assertTrue; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.View; import android.view.ViewGroup.OnHierarchyChangeListener; -import com.android.frameworks.coretests.R; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.Future; +import com.android.frameworks.coretests.R; import org.junit.Before; import org.junit.Rule; @@ -43,6 +39,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + /** * Tests for AppWidgetHostView */ diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java index 8e73b521cdfc..01e82a5a9f7f 100644 --- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java +++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java @@ -18,7 +18,8 @@ package android.widget; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase2; -import android.test.FlakyTest; + +import androidx.test.filters.FlakyTest; // TODO: tests fail intermittently. Add back MediumTest annotation when fixed public class AutoCompleteTextViewCallbacks @@ -32,7 +33,7 @@ public class AutoCompleteTextViewCallbacks /** Test that the initial popup of the suggestions does not select anything. */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupNoSelection() throws Exception { AutoCompleteTextViewSimple theActivity = getActivity(); AutoCompleteTextView textView = theActivity.getTextView(); @@ -57,7 +58,7 @@ public class AutoCompleteTextViewCallbacks } /** Test that arrow-down into the popup calls the onSelected callback. */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupEnterSelection() throws Exception { final AutoCompleteTextViewSimple theActivity = getActivity(); AutoCompleteTextView textView = theActivity.getTextView(); @@ -106,7 +107,7 @@ public class AutoCompleteTextViewCallbacks } /** Test that arrow-up out of the popup calls the onNothingSelected callback */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupLeaveSelection() { final AutoCompleteTextViewSimple theActivity = getActivity(); AutoCompleteTextView textView = theActivity.getTextView(); diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java index ee0abaea027a..21e4184e8e4d 100644 --- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java +++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java @@ -18,8 +18,8 @@ package android.widget; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase2; -import android.test.FlakyTest; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.FlakyTest; /** * A collection of tests on aspects of the AutoCompleteTextView's popup @@ -41,7 +41,7 @@ public class AutoCompleteTextViewPopup } /** Test that we can move the selection and it responds as expected */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupSetListSelection() throws Throwable { AutoCompleteTextViewSimple theActivity = getActivity(); final AutoCompleteTextView textView = theActivity.getTextView(); @@ -73,7 +73,7 @@ public class AutoCompleteTextViewPopup } /** Test that we can look at the selection as we move around */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupGetListSelection() throws Throwable { AutoCompleteTextViewSimple theActivity = getActivity(); final AutoCompleteTextView textView = theActivity.getTextView(); @@ -100,7 +100,7 @@ public class AutoCompleteTextViewPopup } /** Test that we can clear the selection */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupClearListSelection() throws Throwable { AutoCompleteTextViewSimple theActivity = getActivity(); final AutoCompleteTextView textView = theActivity.getTextView(); @@ -133,7 +133,7 @@ public class AutoCompleteTextViewPopup } /** Make sure we handle an empty adapter properly */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupNavigateNoAdapter() throws Throwable { AutoCompleteTextViewSimple theActivity = getActivity(); final AutoCompleteTextView textView = theActivity.getTextView(); @@ -167,7 +167,7 @@ public class AutoCompleteTextViewPopup } /** Test the show/hide behavior of the drop-down. */ - @FlakyTest(tolerance=3) + @FlakyTest public void testPopupShow() throws Throwable { AutoCompleteTextViewSimple theActivity = getActivity(); final AutoCompleteTextView textView = theActivity.getTextView(); diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java index b4e05aa26de7..063bec5fa7cc 100644 --- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java +++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java @@ -16,8 +16,6 @@ package android.widget; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.util.Log; @@ -25,6 +23,8 @@ import android.view.View; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; +import com.android.frameworks.coretests.R; + public class AutoCompleteTextViewSimple extends Activity implements OnItemClickListener, OnItemSelectedListener { diff --git a/core/tests/coretests/src/android/widget/DatePickerActivity.java b/core/tests/coretests/src/android/widget/DatePickerActivity.java index c3b25a1a5a8c..9e455b223865 100644 --- a/core/tests/coretests/src/android/widget/DatePickerActivity.java +++ b/core/tests/coretests/src/android/widget/DatePickerActivity.java @@ -18,6 +18,7 @@ package android.widget; import android.app.Activity; import android.os.Bundle; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java index be85450be429..f0672304d90e 100644 --- a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java +++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java @@ -19,11 +19,12 @@ package android.widget; import android.app.Activity; import android.app.Instrumentation; import android.os.SystemClock; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase2; import android.view.KeyEvent; import android.view.View; +import androidx.test.filters.LargeTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/widget/DateTimeViewTest.java b/core/tests/coretests/src/android/widget/DateTimeViewTest.java index 40a6b7a43f53..d0bd4b8b0bd5 100644 --- a/core/tests/coretests/src/android/widget/DateTimeViewTest.java +++ b/core/tests/coretests/src/android/widget/DateTimeViewTest.java @@ -16,10 +16,10 @@ package android.widget; -import android.support.test.InstrumentationRegistry; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.InstrumentationRegistry; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java index 9186827b38f3..e4f55df6f683 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java @@ -31,10 +31,11 @@ import static org.hamcrest.Matchers.sameInstance; import android.app.Activity; import android.app.Instrumentation; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/widget/ListViewTest.java b/core/tests/coretests/src/android/widget/ListViewTest.java index 449b696fb008..254af2aed768 100644 --- a/core/tests/coretests/src/android/widget/ListViewTest.java +++ b/core/tests/coretests/src/android/widget/ListViewTest.java @@ -16,20 +16,19 @@ package android.widget; -import android.test.suitebuilder.annotation.Suppress; -import com.google.android.collect.Lists; - -import junit.framework.Assert; - import android.content.Context; import android.content.res.Resources; import android.test.InstrumentationTestCase; import android.test.mock.MockContext; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ListView; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + +import com.google.android.collect.Lists; + +import junit.framework.Assert; import java.util.List; diff --git a/core/tests/coretests/src/android/widget/RadioGroupActivity.java b/core/tests/coretests/src/android/widget/RadioGroupActivity.java index c87aa3ad609d..dd3b30a7b326 100644 --- a/core/tests/coretests/src/android/widget/RadioGroupActivity.java +++ b/core/tests/coretests/src/android/widget/RadioGroupActivity.java @@ -17,11 +17,11 @@ package android.widget; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + public class RadioGroupActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java index 1ab362835bbe..18c1ea164ebb 100644 --- a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java +++ b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java @@ -16,12 +16,13 @@ package android.widget; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase2; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; /** * Exercises {@link android.widget.RadioGroup}'s check feature. diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java index 06b860a96117..da53f6d94f49 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java @@ -38,11 +38,12 @@ import android.database.DataSetObserver; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.View; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.R; import com.android.internal.widget.IRemoteViewsFactory; diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 36792bbf6fb6..8cb7e1b95245 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -32,12 +32,13 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Binder; import android.os.Parcel; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.View; import android.view.ViewGroup; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.R; import org.junit.Before; diff --git a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java index 2add22105681..74aad9afa359 100644 --- a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java +++ b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java @@ -22,7 +22,8 @@ import static java.util.function.Function.identity; import android.graphics.PointF; import android.graphics.RectF; -import android.support.test.filters.LargeTest; + +import androidx.test.filters.LargeTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java b/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java index 1731c08df018..a79b30dbbe1a 100644 --- a/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java +++ b/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java @@ -16,14 +16,15 @@ package android.widget; -import android.test.suitebuilder.annotation.Suppress; -import com.google.android.collect.Lists; - import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.SmallTest; +import androidx.test.filters.Suppress; + +import com.google.android.collect.Lists; import java.util.ArrayList; import java.util.Random; diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java index eafe42723780..483270e1f9ab 100644 --- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java +++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java @@ -36,13 +36,13 @@ import static android.widget.espresso.SuggestionsPopupwindowUtils.clickSuggestio import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsPopup; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; + import static org.hamcrest.Matchers.is; + import android.content.res.TypedArray; import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.ViewAssertion; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; import android.text.Selection; import android.text.Spannable; import android.text.Spanned; @@ -51,6 +51,8 @@ import android.text.style.SuggestionSpan; import android.text.style.TextAppearanceSpan; import android.view.View; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java index ff4a7dafc627..41fa08b7e0e2 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java @@ -43,14 +43,15 @@ import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIn import static android.widget.espresso.TextViewAssertions.hasSelection; import android.app.Activity; -import android.support.test.filters.MediumTest; -import android.support.test.filters.Suppress; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.view.MotionEvent; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.R; import org.junit.Before; diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 90758ba7c418..9d93421b1783 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -27,10 +27,8 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates; import static android.widget.espresso.DragHandleUtils.onHandleView; -import static android.widget.espresso.FloatingToolbarEspressoUtils - .assertFloatingToolbarContainsItem; -import static android.widget.espresso.FloatingToolbarEspressoUtils - .assertFloatingToolbarDoesNotContainItem; +import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem; +import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex; import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem; @@ -62,13 +60,8 @@ import android.app.Activity; import android.app.Instrumentation; import android.content.ClipData; import android.content.ClipboardManager; -import android.support.test.InstrumentationRegistry; import android.support.test.espresso.action.EspressoKey; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; -import android.test.suitebuilder.annotation.Suppress; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -85,6 +78,12 @@ import android.view.textclassifier.TextLinks; import android.view.textclassifier.TextLinksParams; import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.R; import org.junit.Before; diff --git a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java index e9d1d3e00e8a..113db9d7a2b7 100644 --- a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java +++ b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java @@ -21,8 +21,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.app.Activity; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; import android.text.DynamicLayout; import android.text.FontFallbackSetup; import android.text.Layout; @@ -31,6 +29,9 @@ import android.util.TypedValue; import android.view.View; import android.widget.TextView.BufferType; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java index cf173fb78a32..a769ea40b4f2 100644 --- a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java +++ b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java @@ -20,14 +20,15 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.MediumTest; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.SpannedString; import android.view.View; import android.view.ViewGroup; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 1c5610b1bc7d..585360fdd2f3 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -27,11 +27,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.Paint; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.MediumTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.text.GetChars; import android.text.Layout; import android.text.PrecomputedText; @@ -40,6 +35,12 @@ import android.text.Spannable; import android.view.View; import android.widget.TextView.BufferType; +import androidx.test.InstrumentationRegistry; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; diff --git a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java index 75da6fef46ff..85a4509c18a3 100644 --- a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java +++ b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java @@ -16,12 +16,11 @@ package android.widget.focus; -import android.util.InternalSelectionView; - import android.app.Activity; import android.os.Bundle; -import android.widget.LinearLayout; +import android.util.InternalSelectionView; import android.view.ViewGroup; +import android.widget.LinearLayout; /** * {@link android.view.FocusFinder#findNextFocus(android.view.ViewGroup, android.view.View, int)} diff --git a/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java b/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java index f6b0520fbadb..fe6d3c8393f1 100644 --- a/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java +++ b/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java @@ -16,13 +16,13 @@ package android.widget.focus; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.view.ViewGroup; import android.widget.Button; +import com.android.frameworks.coretests.R; + public class DescendantFocusability extends Activity { public ViewGroup beforeDescendants; diff --git a/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java b/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java index 2af42ac193c0..1c570df94f59 100644 --- a/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java +++ b/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java @@ -16,15 +16,14 @@ package android.widget.focus; -import android.widget.focus.DescendantFocusability; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.UiThreadTest; import android.test.TouchUtils; import android.view.ViewGroup; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class DescendantFocusabilityTest extends ActivityInstrumentationTestCase<DescendantFocusability> { private DescendantFocusability a; diff --git a/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java b/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java index b3d5ec5e5099..0cb80cebffc7 100644 --- a/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java +++ b/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java @@ -16,13 +16,13 @@ package android.widget.focus; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.widget.LinearLayout; -import android.widget.Button; import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +import com.android.frameworks.coretests.R; /** * Exercises cases where elements of the UI are removed (and diff --git a/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java b/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java index a1b7bcbf6487..6c46d08bc223 100644 --- a/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java +++ b/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java @@ -16,15 +16,15 @@ package android.widget.focus; -import android.widget.focus.FocusAfterRemoval; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.Button; -import android.widget.LinearLayout; import android.view.KeyEvent; import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; /** * {@link FocusAfterRemoval} is set up to exercise cases where the views that diff --git a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java index 8f8f184a0c9c..26dc23398366 100644 --- a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java +++ b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java @@ -16,14 +16,13 @@ package android.widget.focus; -import android.widget.focus.AdjacentVerticalRectLists; -import android.util.InternalSelectionView; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; +import android.util.InternalSelectionView; import android.view.KeyEvent; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + /** * {@link android.view.FocusFinder#findNextFocus(android.view.ViewGroup, android.view.View, int)} * and diff --git a/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java b/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java index dcbddefef870..b7974e38555a 100644 --- a/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java +++ b/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java @@ -17,10 +17,10 @@ package android.widget.focus; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.view.View; -import android.widget.focus.GoneParentFocusedChild; + +import androidx.test.filters.MediumTest; /** * When a parent is GONE, key events shouldn't go to its children, even if they diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java index 11cac1e5b1ea..aab91194cfd7 100644 --- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java +++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java @@ -17,12 +17,12 @@ package android.widget.focus; import android.app.Activity; -import android.widget.LinearLayout; -import android.widget.Button; -import android.widget.TextView; +import android.content.Context; import android.os.Bundle; import android.view.ViewGroup; -import android.content.Context; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; public class HorizontalFocusSearch extends Activity { diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java index 43986eed9b14..855a32192555 100644 --- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java +++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java @@ -16,17 +16,16 @@ package android.widget.focus; -import android.widget.focus.HorizontalFocusSearch; +import static android.widget.focus.VerticalFocusSearchTest.FocusSearchAlg; +import static android.widget.focus.VerticalFocusSearchTest.NewFocusSearchAlg; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.LinearLayout; -import android.widget.Button; import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; -import static android.widget.focus.VerticalFocusSearchTest.FocusSearchAlg; -import static android.widget.focus.VerticalFocusSearchTest.NewFocusSearchAlg; +import androidx.test.filters.LargeTest; +import androidx.test.filters.Suppress; /** * Tests that focus searching works on a horizontal linear layout of buttons of diff --git a/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java b/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java index acd632f3402b..ec234bc616df 100644 --- a/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java +++ b/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java @@ -21,6 +21,7 @@ import android.os.Bundle; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; + import com.android.frameworks.coretests.R; public class LinearLayoutGrid extends Activity { diff --git a/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java b/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java index 89cb8bb6943e..c81317c003b4 100644 --- a/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java +++ b/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java @@ -17,11 +17,11 @@ package android.widget.focus; import android.test.SingleLaunchActivityTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.FocusFinder; import android.view.View; import android.view.ViewGroup; -import android.widget.focus.LinearLayoutGrid; + +import androidx.test.filters.MediumTest; /** * Tests focus searching between buttons within a grid that are touching, for example, diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtons.java b/core/tests/coretests/src/android/widget/focus/ListOfButtons.java index 308861d3d9df..5663ed2094f7 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfButtons.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfButtons.java @@ -16,8 +16,6 @@ package android.widget.focus; -import com.android.frameworks.coretests.R; - import android.app.ListActivity; import android.content.Context; import android.os.Bundle; @@ -26,6 +24,8 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; +import com.android.frameworks.coretests.R; + /** * A layout with a ListView containing buttons. */ diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java index bec6f80ddbce..4cf4a3abeb68 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java @@ -16,17 +16,17 @@ package android.widget.focus; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.focus.ListOfButtons; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListAdapter; -import android.widget.Button; -import android.widget.ListView; import android.view.KeyEvent; import android.view.View; +import android.widget.Button; +import android.widget.ListAdapter; +import android.widget.ListView; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + +import com.android.frameworks.coretests.R; /** * Tests that focus works as expected when navigating into and out of diff --git a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java index c2e7a26b5909..936c9999e7f8 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java @@ -21,7 +21,12 @@ import android.content.Context; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; -import android.widget.*; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ListView; + import com.google.android.collect.Lists; import java.util.List; diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java index 53b866c92ef8..73e4ea8f718d 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java @@ -19,11 +19,11 @@ package android.widget.focus; import android.app.Activity; import android.graphics.Point; import android.os.Bundle; +import android.util.InternalSelectionView; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; -import android.util.InternalSelectionView; /** * A list of {@link InternalSelectionView}s paramatarized by the number of items, diff --git a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java index b9082011f8b4..8accf21c2adc 100644 --- a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java +++ b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java @@ -27,9 +27,10 @@ import android.widget.BaseAdapter; import android.widget.Button; import android.widget.TextView; -import com.google.android.collect.Lists; import com.android.frameworks.coretests.R; +import com.google.android.collect.Lists; + import java.util.List; public class ListWithFooterViewAndNewLabels extends ListActivity { diff --git a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java index 57dbb78cbcfd..d0fcde5fd49c 100644 --- a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java +++ b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java @@ -16,14 +16,13 @@ package android.widget.focus; -import android.widget.focus.ListWithFooterViewAndNewLabels; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; import android.widget.Button; import android.widget.ListAdapter; import android.widget.ListView; +import com.android.frameworks.coretests.R; + public class ListWithFooterViewAndNewLabelsTest extends ActivityInstrumentationTestCase<ListWithFooterViewAndNewLabels> { diff --git a/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java b/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java index 5c891f90bc2f..50a8614a1992 100644 --- a/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java +++ b/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java @@ -16,19 +16,20 @@ package android.widget.focus; -import com.android.frameworks.coretests.R; -import com.google.android.collect.Lists; - import android.app.ListActivity; -import android.os.Bundle; -import android.widget.ArrayAdapter; -import android.widget.LinearLayout; -import android.widget.TextView; import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.webkit.WebView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.frameworks.coretests.R; + +import com.google.android.collect.Lists; import java.util.List; diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java index 4daf0b4206b0..5042efd371e5 100644 --- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java +++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java @@ -16,13 +16,13 @@ package android.widget.focus; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.widget.Button; +import com.android.frameworks.coretests.R; + /** * Exercises cases where elements of the UI are requestFocus()ed. */ diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java index cdfa21755344..bc770c502a2b 100644 --- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java +++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java @@ -21,16 +21,18 @@ import static org.mockito.Mockito.mock; import android.os.Handler; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.util.AndroidRuntimeException; import android.view.View; import android.view.View.OnFocusChangeListener; import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; import android.widget.Button; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; + import org.mockito.InOrder; /** diff --git a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java index 06cb75db28c2..e6e76ccc2caf 100644 --- a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java +++ b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java @@ -18,12 +18,12 @@ package android.widget.focus; import android.graphics.Rect; import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.util.InternalSelectionView; import android.view.KeyEvent; import android.widget.ListView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; /** * TODO: extract base test case that launches {@link ListOfInternalSelectionViews} with diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java index 4a809e0cc881..2ca9f6da6404 100644 --- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java +++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java @@ -17,13 +17,13 @@ package android.widget.focus; import android.app.Activity; +import android.content.Context; import android.os.Bundle; -import android.widget.LinearLayout; -import android.widget.Button; -import android.widget.TextView; import android.view.Gravity; import android.view.ViewGroup; -import android.content.Context; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; /** * Holds a few buttons of various sizes and horizontal placements in a diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java index f01422ebfc52..319756cc5876 100644 --- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java +++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java @@ -16,17 +16,16 @@ package android.widget.focus; -import android.widget.focus.VerticalFocusSearch; - -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.Suppress; import android.view.FocusFinder; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; +import androidx.test.filters.LargeTest; +import androidx.test.filters.Suppress; + /** * Tests that focus searching works on a vertical linear layout of buttons of * various widths and horizontal placements. diff --git a/core/tests/coretests/src/android/widget/gridview/GridDelete.java b/core/tests/coretests/src/android/widget/gridview/GridDelete.java index 57ae8f39fa09..b040f69545f1 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridDelete.java +++ b/core/tests/coretests/src/android/widget/gridview/GridDelete.java @@ -16,6 +16,7 @@ package android.widget.gridview; +import android.util.GridScenario; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -23,8 +24,6 @@ import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ListAdapter; -import android.util.GridScenario; - import java.util.ArrayList; /** diff --git a/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java b/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java index 21ca655390a7..5247009e693b 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java @@ -17,10 +17,9 @@ package android.widget.gridview; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.GridView; -import android.widget.gridview.GridInHorizontal; +import androidx.test.filters.MediumTest; public class GridInHorizontalTest extends ActivityInstrumentationTestCase<GridInHorizontal> { diff --git a/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java b/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java index a674db25f708..290f70ee639b 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java @@ -17,10 +17,9 @@ package android.widget.gridview; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.GridView; -import android.widget.gridview.GridInVertical; +import androidx.test.filters.MediumTest; public class GridInVerticalTest extends ActivityInstrumentationTestCase<GridInVertical> { diff --git a/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java b/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java index ecd4b1cbbad3..d2ae7cfc6b25 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java @@ -17,9 +17,10 @@ package android.widget.gridview; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.GridView; +import androidx.test.filters.MediumTest; + public class GridPaddingTest extends ActivityInstrumentationTestCase2<GridPadding> { private GridView mGridView; diff --git a/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java b/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java index 53eeb48212ce..466c55f8117c 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java @@ -19,12 +19,13 @@ package android.widget.gridview; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.AbsListView; import android.widget.GridView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class GridScrollListenerTest extends ActivityInstrumentationTestCase<GridScrollListener> implements AbsListView.OnScrollListener { private GridScrollListener mActivity; diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java index 0e362b69b057..db4f2dc224fc 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java @@ -16,13 +16,13 @@ package android.widget.gridview; -import android.util.GridScenario; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; +import android.util.GridScenario; import android.widget.GridView; +import androidx.test.filters.MediumTest; + public class GridSetSelectionBaseTest<T extends GridScenario> extends ActivityInstrumentationTestCase<T> { private T mActivity; private GridView mGridView; diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java index 67396451898c..17a60449e766 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java @@ -16,8 +16,6 @@ package android.widget.gridview; -import android.widget.gridview.GridSetSelectionMany; - public class GridSetSelectionManyTest extends GridSetSelectionBaseTest<GridSetSelectionMany> { public GridSetSelectionManyTest() { super(GridSetSelectionMany.class); diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java index 46922b97f6fd..a71f732c207f 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java @@ -16,8 +16,6 @@ package android.widget.gridview; -import android.widget.gridview.GridSetSelectionStackFromBottomMany; - public class GridSetSelectionStackFromBottomManyTest extends GridSetSelectionBaseTest<GridSetSelectionStackFromBottomMany> { public GridSetSelectionStackFromBottomManyTest() { super(GridSetSelectionStackFromBottomMany.class); diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java index 67dd6f10a86e..4e6e8aa58912 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java @@ -16,8 +16,6 @@ package android.widget.gridview; -import android.widget.gridview.GridSetSelectionStackFromBottom; - public class GridSetSelectionStackFromBottomTest extends GridSetSelectionBaseTest<GridSetSelectionStackFromBottom> { public GridSetSelectionStackFromBottomTest() { super(GridSetSelectionStackFromBottom.class); diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java index 2127b3cd4f1a..68802fe22f95 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java @@ -16,8 +16,6 @@ package android.widget.gridview; -import android.widget.gridview.GridSetSelection; - public class GridSetSelectionTest extends GridSetSelectionBaseTest<GridSetSelection> { public GridSetSelectionTest() { super(GridSetSelection.class); diff --git a/core/tests/coretests/src/android/widget/gridview/GridSimple.java b/core/tests/coretests/src/android/widget/gridview/GridSimple.java index 7c2c696f2b56..67bb7511122a 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSimple.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSimple.java @@ -18,12 +18,11 @@ package android.widget.gridview; import android.graphics.drawable.PaintDrawable; import android.os.Bundle; +import android.util.GridScenario; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import android.util.GridScenario; - public class GridSimple extends GridScenario { @Override protected void init(Params params) { diff --git a/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java b/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java index 3b2504ebe3be..0f67522894cc 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java @@ -17,10 +17,9 @@ package android.widget.gridview; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.GridView; -import android.widget.gridview.GridSingleColumn; +import androidx.test.filters.MediumTest; public class GridSingleColumnTest extends ActivityInstrumentationTestCase<GridSingleColumn> { private GridSingleColumn mActivity; diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java index 640737ec1c85..a3696164526e 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java @@ -16,11 +16,10 @@ package android.widget.gridview; -import android.widget.gridview.GridStackFromBottomMany; - -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.GridView; import android.test.ActivityInstrumentationTestCase; +import android.widget.GridView; + +import androidx.test.filters.MediumTest; public class GridStackFromBottomManyTest extends ActivityInstrumentationTestCase<GridStackFromBottomMany> { private GridStackFromBottomMany mActivity; diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java index 8fec241dd304..da1b6384d212 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java @@ -16,12 +16,11 @@ package android.widget.gridview; -import android.widget.gridview.GridStackFromBottom; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.GridView; +import androidx.test.filters.MediumTest; + public class GridStackFromBottomTest extends ActivityInstrumentationTestCase<GridStackFromBottom> { private GridStackFromBottom mActivity; private GridView mGridView; diff --git a/core/tests/coretests/src/android/widget/gridview/GridThrasher.java b/core/tests/coretests/src/android/widget/gridview/GridThrasher.java index ad89bb6ab90c..34c19c37b702 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridThrasher.java +++ b/core/tests/coretests/src/android/widget/gridview/GridThrasher.java @@ -16,20 +16,20 @@ package android.widget.gridview; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Handler; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.TextView; +import com.android.frameworks.coretests.R; + import java.util.Random; /** diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java index ca789aff672e..ab5fcfaf29ad 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java @@ -17,14 +17,14 @@ package android.widget.gridview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.View; import android.widget.GridView; - import android.widget.gridview.GridSimple; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + /** * Tests setting the selection in touch mode */ diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java index 9a8d3074890b..e312873d4ae1 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java @@ -16,14 +16,14 @@ package android.widget.gridview.touch; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; +import android.test.ActivityInstrumentationTestCase; import android.test.TouchUtils; +import android.view.View; +import android.widget.GridView; import android.widget.gridview.GridStackFromBottomMany; -import android.widget.GridView; -import android.view.View; -import android.test.ActivityInstrumentationTestCase; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; public class GridTouchStackFromBottomManyTest extends ActivityInstrumentationTestCase<GridStackFromBottomMany> { private GridStackFromBottomMany mActivity; diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java index d8d4e435d2c7..c98c10a603f4 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java @@ -16,13 +16,13 @@ package android.widget.gridview.touch; -import android.widget.gridview.GridStackFromBottom; -import android.test.TouchUtils; -import android.test.suitebuilder.annotation.MediumTest; - import android.test.ActivityInstrumentationTestCase; -import android.widget.GridView; +import android.test.TouchUtils; import android.view.View; +import android.widget.GridView; +import android.widget.gridview.GridStackFromBottom; + +import androidx.test.filters.MediumTest; public class GridTouchStackFromBottomTest extends ActivityInstrumentationTestCase<GridStackFromBottom> { private GridStackFromBottom mActivity; diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java index 55a66d9c2af2..0d3092cbc2f3 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java @@ -18,16 +18,16 @@ package android.widget.gridview.touch; import android.content.Context; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.Gravity; import android.view.View; import android.view.ViewConfiguration; import android.widget.GridView; - import android.widget.gridview.GridVerticalSpacingStackFromBottom; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class GridTouchVerticalSpacingStackFromBottomTest extends ActivityInstrumentationTestCase<GridVerticalSpacingStackFromBottom> { private GridVerticalSpacingStackFromBottom mActivity; private GridView mGridView; diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java index bae4ee7293e6..e831e62be179 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java @@ -17,16 +17,16 @@ package android.widget.gridview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.Gravity; import android.view.View; import android.view.ViewConfiguration; import android.widget.GridView; - import android.widget.gridview.GridVerticalSpacing; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class GridTouchVerticalSpacingTest extends ActivityInstrumentationTestCase<GridVerticalSpacing> { private GridVerticalSpacing mActivity; private GridView mGridView; diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java index 9791e36f038d..d159be865372 100644 --- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java +++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java @@ -16,11 +16,10 @@ package android.widget.layout.frame; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.View; + +import com.android.frameworks.coretests.R; public class FrameLayoutGravity extends Activity { @Override diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java index fe4e93286bc5..0cab660f68d6 100644 --- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java +++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java @@ -16,12 +16,13 @@ package android.widget.layout.frame; +import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; -import android.app.Activity; import android.view.View; -import android.widget.layout.frame.FrameLayoutGravity; + +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; public class FrameLayoutGravityTest extends ActivityInstrumentationTestCase<FrameLayoutGravity> { diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java index 81b3ea13fc6b..8c54557819d0 100644 --- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java +++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java @@ -16,11 +16,10 @@ package android.widget.layout.frame; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.View; + +import com.android.frameworks.coretests.R; public class FrameLayoutMargin extends Activity { @Override diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java index c052d659c0f2..4973078f9752 100644 --- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java +++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java @@ -16,13 +16,14 @@ package android.widget.layout.frame; +import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; -import android.app.Activity; import android.view.View; import android.view.ViewGroup; -import android.widget.layout.frame.FrameLayoutMargin; + +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; public class FrameLayoutMarginTest extends ActivityInstrumentationTestCase<FrameLayoutMargin> { diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java index 766dd0a76366..79a34e5d1f5e 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java @@ -16,11 +16,10 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.View; + +import com.android.frameworks.coretests.R; public class BaselineAlignmentCenterGravity extends Activity { @Override diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java index 079e9d079ee5..60b7c2f5e8df 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java @@ -18,13 +18,13 @@ package android.widget.layout.linear; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; import android.view.View; import android.widget.Button; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; -import android.widget.layout.linear.BaselineAlignmentCenterGravity; public class BaselineAlignmentCenterGravityTest extends ActivityInstrumentationTestCase<BaselineAlignmentCenterGravity> { private Button mButton1; diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java index c3bfe3a30ee8..a429b758d748 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java @@ -18,12 +18,12 @@ package android.widget.layout.linear; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; import android.view.View; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; -import android.widget.layout.linear.HorizontalOrientationVerticalAlignment; public class BaselineAlignmentSpinnerButton extends ActivityInstrumentationTestCase<HorizontalOrientationVerticalAlignment> { private View mSpinner; diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java index 5ed5e71d65a7..f33feb0c91a8 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java @@ -16,12 +16,12 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.view.View; +import com.android.frameworks.coretests.R; + public class BaselineAlignmentZeroWidthAndWeight extends Activity { @Override protected void onCreate(Bundle icicle) { diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java index 2dd2bb8aa382..443b0f7d6ef0 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java @@ -16,16 +16,15 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; -import android.widget.layout.linear.BaselineAlignmentZeroWidthAndWeight; -import android.widget.layout.linear.ExceptionTextView; - import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.Button; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + public class BaselineAlignmentZeroWidthAndWeightTest extends ActivityInstrumentationTestCase<BaselineAlignmentZeroWidthAndWeight> { private Button mShowButton; diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java index c9ad83174e63..9d9131675004 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java @@ -16,11 +16,10 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.View; + +import com.android.frameworks.coretests.R; public class BaselineButtons extends Activity { @Override diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java index 6f1fc90c001b..ac3c5d8de53c 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java @@ -18,12 +18,12 @@ package android.widget.layout.linear; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; import android.widget.ImageButton; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; -import android.widget.layout.linear.BaselineButtons; public class BaselineButtonsTest extends ActivityInstrumentationTestCase<BaselineButtons> { private View mCurrentTime; diff --git a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java index 6129b5b4c7e9..c68450188456 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java +++ b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java @@ -21,7 +21,6 @@ import android.text.BoringLayout; import android.util.AttributeSet; import android.widget.EditText; - /** * A special EditText that sets {@link #isFailed()} to true as its internal makeNewLayout() method is called * with a width lower than 0. This is used to fail the unit test in diff --git a/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java b/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java index 50aa5b721eca..c8a5f3451d17 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java +++ b/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java @@ -16,12 +16,12 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.widget.TextView; +import com.android.frameworks.coretests.R; + public class FillInWrap extends Activity { @Override protected void onCreate(Bundle icicle) { diff --git a/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java b/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java index f16180248a5e..0e69efc1349e 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java @@ -18,9 +18,10 @@ package android.widget.layout.linear; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; public class FillInWrapTest extends ActivityInstrumentationTestCase<FillInWrap> { diff --git a/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java b/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java index 9f937a97b5a6..255f7b37dba6 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java +++ b/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java @@ -16,11 +16,10 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.View; + +import com.android.frameworks.coretests.R; public class HorizontalOrientationVerticalAlignment extends Activity { @Override diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java b/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java index 83331caa6069..a0745dd1aa45 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java +++ b/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java @@ -16,14 +16,14 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; +import com.android.frameworks.coretests.R; + public class LLEditTextThenButton extends Activity { private EditText mEditText; private Button mButton; diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java index ab2f060eafc2..115306225e75 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java +++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java @@ -18,10 +18,10 @@ package android.widget.layout.linear; import android.app.Activity; import android.os.Bundle; -import android.os.RemoteException; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; + import com.android.frameworks.coretests.R; /** diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java index 77f564db0ef6..0aca699e3c83 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java +++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java @@ -22,5 +22,4 @@ package android.widget.layout.linear; */ public class LLOfButtons2 extends LLOfButtons1 { - } diff --git a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java index 90db788e45eb..5d372450f5ac 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java +++ b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java @@ -16,11 +16,11 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + public class LinearLayoutEditTexts extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java index d5998b7c954d..966ed6dab8b9 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java @@ -16,13 +16,13 @@ package android.widget.layout.linear; -import android.widget.layout.linear.LinearLayoutEditTexts; -import com.android.frameworks.coretests.R; - +import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; -import android.app.Activity; + +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; public class LinearLayoutEditTextsTest extends ActivityInstrumentationTestCase<LinearLayoutEditTexts> { private View mChild; diff --git a/core/tests/coretests/src/android/widget/layout/linear/Weight.java b/core/tests/coretests/src/android/widget/layout/linear/Weight.java index 20edd7c7e31d..3722a14f86fe 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/Weight.java +++ b/core/tests/coretests/src/android/widget/layout/linear/Weight.java @@ -16,11 +16,11 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + public class Weight extends Activity { @Override protected void onCreate(Bundle icicle) { diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java b/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java index 2e421daeaf1c..144600beddf6 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java +++ b/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java @@ -16,11 +16,10 @@ package android.widget.layout.linear; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; -import android.view.View; + +import com.android.frameworks.coretests.R; public class WeightSum extends Activity { @Override diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java b/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java index f9a94cedd4a2..1d07db236436 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java @@ -18,11 +18,11 @@ package android.widget.layout.linear; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; -import android.widget.layout.linear.WeightSum; public class WeightSumTest extends ActivityInstrumentationTestCase<WeightSum> { private View mChild; diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java b/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java index 1c42e7ccb9be..db524958db47 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java +++ b/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java @@ -18,13 +18,13 @@ package android.widget.layout.linear; import android.app.Activity; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; -import android.test.suitebuilder.annotation.Suppress; import android.view.View; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + import com.android.frameworks.coretests.R; -import android.widget.layout.linear.Weight; @Suppress // Failing. public class WeightTest extends ActivityInstrumentationTestCase<Weight> { diff --git a/core/tests/coretests/src/android/widget/layout/table/AddColumn.java b/core/tests/coretests/src/android/widget/layout/table/AddColumn.java index 400c32c0fed5..b407941b411c 100644 --- a/core/tests/coretests/src/android/widget/layout/table/AddColumn.java +++ b/core/tests/coretests/src/android/widget/layout/table/AddColumn.java @@ -16,8 +16,6 @@ package android.widget.layout.table; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; import android.view.View; @@ -26,6 +24,8 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; +import com.android.frameworks.coretests.R; + /** * This test adds an extra row with an extra column in the table. */ diff --git a/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java b/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java index bfb4d172f6c3..08ae0301da13 100644 --- a/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java +++ b/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java @@ -16,16 +16,16 @@ package android.widget.layout.table; -import android.widget.layout.table.AddColumn; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.Button; import android.widget.TableLayout; import android.widget.TableRow; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * {@link android.widget.layout.table.AddColumn} is * setup to exercise the case of adding row programmatically in a table. diff --git a/core/tests/coretests/src/android/widget/layout/table/CellSpan.java b/core/tests/coretests/src/android/widget/layout/table/CellSpan.java index d91cf56eba2e..3102ac1f109d 100644 --- a/core/tests/coretests/src/android/widget/layout/table/CellSpan.java +++ b/core/tests/coretests/src/android/widget/layout/table/CellSpan.java @@ -16,11 +16,11 @@ package android.widget.layout.table; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + /** * Exercise table layout with cells spanning. */ diff --git a/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java b/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java index 331ec4575395..aa8e66cb0c67 100644 --- a/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java +++ b/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java @@ -16,13 +16,13 @@ package android.widget.layout.table; -import android.widget.layout.table.CellSpan; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * {@link android.widget.layout.table.CellSpan} is * setup to exercise tables in which cells use spanning. diff --git a/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java b/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java index 435815a40d06..c587f74e97f2 100644 --- a/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java +++ b/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java @@ -16,11 +16,11 @@ package android.widget.layout.table; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + /** * Exercise table layout with cells having a fixed width and height. */ diff --git a/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java b/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java index b20ec84ba8aa..7d02453d4e54 100644 --- a/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java +++ b/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java @@ -16,13 +16,13 @@ package android.widget.layout.table; -import android.widget.layout.table.FixedWidth; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * {@link android.widget.layout.table.FixedWidth} is * setup to exercise tables in which cells use fixed width and height. diff --git a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java index 1444f60f4aa9..6f5148be98b7 100644 --- a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java +++ b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java @@ -16,11 +16,11 @@ package android.widget.layout.table; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + /** * Exercise table layout with cells using a horizontal gravity. */ diff --git a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java index 964df823fd55..73e8334e5224 100644 --- a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java +++ b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java @@ -16,14 +16,14 @@ package android.widget.layout.table; -import android.widget.layout.table.HorizontalGravity; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; import android.view.View; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * {@link android.widget.layout.table.HorizontalGravity} is * setup to exercise tables in which cells use horizontal gravity. diff --git a/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java b/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java index 4fdb37894cb8..9055b329a2a6 100644 --- a/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java +++ b/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java @@ -16,11 +16,11 @@ package android.widget.layout.table; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + /** * Exercise table layout with cells using a vertical gravity. */ diff --git a/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java b/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java index 1d6be3f01c79..f14fa1c2d7da 100644 --- a/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java +++ b/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java @@ -16,15 +16,15 @@ package android.widget.layout.table; -import android.widget.layout.table.VerticalGravity; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.test.ViewAsserts; import android.view.View; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + +import com.android.frameworks.coretests.R; + /** * {@link android.widget.layout.table.VerticalGravity} is * setup to exercise tables in which cells use vertical gravity. diff --git a/core/tests/coretests/src/android/widget/layout/table/Weight.java b/core/tests/coretests/src/android/widget/layout/table/Weight.java index 6d4d51d93a8f..5c0824769d7a 100644 --- a/core/tests/coretests/src/android/widget/layout/table/Weight.java +++ b/core/tests/coretests/src/android/widget/layout/table/Weight.java @@ -16,11 +16,11 @@ package android.widget.layout.table; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import com.android.frameworks.coretests.R; + /** * Exercise table layout with cells having a weight. */ diff --git a/core/tests/coretests/src/android/widget/layout/table/WeightTest.java b/core/tests/coretests/src/android/widget/layout/table/WeightTest.java index b6655735d38c..fcf3de20e1d8 100644 --- a/core/tests/coretests/src/android/widget/layout/table/WeightTest.java +++ b/core/tests/coretests/src/android/widget/layout/table/WeightTest.java @@ -16,13 +16,13 @@ package android.widget.layout.table; -import android.widget.layout.table.Weight; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.View; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + /** * {@link android.widget.layout.table.Weight} is * setup to exercise tables in which cells use a weight. diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java index a386ebd3d941..cd76d70acdaf 100644 --- a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java +++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java @@ -16,8 +16,6 @@ package android.widget.listview; -import android.view.Gravity; - import android.util.ListScenario; /** diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java index 519816c731a5..e048e3eb98a0 100644 --- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java +++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java @@ -16,8 +16,6 @@ package android.widget.listview; -import android.view.Gravity; - import android.util.ListScenario; /** diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java index e1171eb0982e..bd8dbe43947b 100644 --- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java @@ -17,10 +17,9 @@ package android.widget.listview; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.ListView; -import android.widget.listview.ListBottomGravityMany; +import androidx.test.filters.MediumTest; public class ListBottomGravityManyTest extends ActivityInstrumentationTestCase<ListBottomGravityMany> { private ListBottomGravityMany mActivity; diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java index c595f62bd858..8da7358c04f6 100644 --- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java @@ -17,10 +17,9 @@ package android.widget.listview; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.ListView; -import android.widget.listview.ListBottomGravity; +import androidx.test.filters.MediumTest; public class ListBottomGravityTest extends ActivityInstrumentationTestCase<ListBottomGravity> { private ListBottomGravity mActivity; diff --git a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java index bbed73c65af2..86407411c2d0 100644 --- a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java +++ b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java @@ -16,10 +16,10 @@ package android.widget.listview; -import android.util.ListItemFactory; import static android.util.ListItemFactory.Slot; -import android.util.ListScenario; +import android.util.ListItemFactory; +import android.util.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java b/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java index 258d3efc0c01..81d71a91edc3 100644 --- a/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java @@ -19,12 +19,13 @@ package android.widget.listview; import android.app.Instrumentation; import android.content.Intent; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class ListEmptyViewTest extends ActivityInstrumentationTestCase<ListWithEmptyView> { private ListWithEmptyView mActivity; private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/ListFilter.java b/core/tests/coretests/src/android/widget/listview/ListFilter.java index c2ac90e7c47c..1cda71714ca5 100644 --- a/core/tests/coretests/src/android/widget/listview/ListFilter.java +++ b/core/tests/coretests/src/android/widget/listview/ListFilter.java @@ -25,7 +25,6 @@ import android.widget.Button; import com.android.frameworks.coretests.R; - /** * Tests hiding and showing the list filter by hiding and showing an ancestor of the * ListView diff --git a/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java b/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java index bf18a13dc4f1..10b4a79f40aa 100644 --- a/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java @@ -17,10 +17,11 @@ package android.widget.listview; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; -import android.widget.ListAdapter; import android.widget.ArrayAdapter; +import android.widget.ListAdapter; +import android.widget.ListView; + +import androidx.test.filters.MediumTest; public class ListFocusableTest extends ActivityInstrumentationTestCase<ListTopGravity> { private ListTopGravity mActivity; diff --git a/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java b/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java index 33d61a093ad4..c691ed753301 100644 --- a/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java @@ -17,10 +17,11 @@ package android.widget.listview; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.ListView; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; + import java.util.Arrays; /** diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java index 1f59c30f20b8..74eda3b850b9 100644 --- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java +++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java @@ -16,11 +16,10 @@ package android.widget.listview; -import android.view.View; -import android.view.ViewGroup; - import android.util.ListItemFactory; import android.util.ListScenario; +import android.view.View; +import android.view.ViewGroup; /** * List that has different view types diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java index 01b39db92a7d..dbd58d7363a8 100644 --- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java @@ -18,12 +18,11 @@ package android.widget.listview; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.ListView; -import android.widget.listview.ListHeterogeneous; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; public class ListHeterogeneousTest extends ActivityInstrumentationTestCase<ListHeterogeneous> { private ListHeterogeneous mActivity; diff --git a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java index 2ff65de9d523..e98de9c1204c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java +++ b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java @@ -16,11 +16,11 @@ package android.widget.listview; -import android.util.ListItemFactory; import static android.util.ListItemFactory.Slot; -import android.util.ListScenario; import android.content.Context; +import android.util.ListItemFactory; +import android.util.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java b/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java index a373a5b19be3..6622c0d21eb2 100644 --- a/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java +++ b/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java @@ -18,10 +18,7 @@ package android.widget.listview; import android.app.Activity; import android.os.Bundle; -import android.os.Handler; import android.widget.ArrayAdapter; -import android.widget.GridView; -import android.widget.TextView; import android.widget.ListView; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java b/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java index 3643f794effd..d8508ecafee5 100644 --- a/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java @@ -16,8 +16,6 @@ package android.widget.listview; -import android.widget.listview.ListInHorizontal; - public class ListInHorizontalTest extends ListUnspecifiedMeasure<ListInHorizontal> { public ListInHorizontalTest() { super(ListInHorizontal.class); diff --git a/core/tests/coretests/src/android/widget/listview/ListInVertical.java b/core/tests/coretests/src/android/widget/listview/ListInVertical.java index 3b4885a9589d..4ecf25d6a818 100644 --- a/core/tests/coretests/src/android/widget/listview/ListInVertical.java +++ b/core/tests/coretests/src/android/widget/listview/ListInVertical.java @@ -18,10 +18,7 @@ package android.widget.listview; import android.app.Activity; import android.os.Bundle; -import android.os.Handler; import android.widget.ArrayAdapter; -import android.widget.GridView; -import android.widget.TextView; import android.widget.ListView; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java b/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java index 85864294573f..5e385a33b057 100644 --- a/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java @@ -16,8 +16,6 @@ package android.widget.listview; -import android.widget.listview.ListInVertical; - public class ListInVerticalTest extends ListUnspecifiedMeasure<ListInVertical> { public ListInVerticalTest() { super(ListInVertical.class); diff --git a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java index d5da28e75c4a..0ec7a2431b93 100644 --- a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java +++ b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java @@ -16,13 +16,14 @@ package android.widget.listview; +import android.util.ListItemFactory; +import android.util.ListScenario; import android.view.View; - import android.view.ViewGroup; - import com.google.android.collect.Sets; - import android.util.ListScenario; - import android.util.ListItemFactory; +import android.view.ViewGroup; - import java.util.Set; +import com.google.android.collect.Sets; + +import java.util.Set; /** * List that interleaves focusable items. diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java index f7c01b1ea975..3159e53eabc5 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java @@ -16,12 +16,10 @@ package android.widget.listview; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; import android.util.ListItemFactory; import android.util.ListScenario; +import android.view.View; +import android.view.ViewGroup; /** * A list where the items may befocusable, but the second item isn't actually focusabe. diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java index b529b2e7b0e8..861e2a919052 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java @@ -16,8 +16,8 @@ package android.widget.listview; -import android.util.ListScenario; import android.util.ListItemFactory; +import android.util.ListScenario; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java index 59987ec4702b..e9c9c1dd586c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java @@ -16,10 +16,10 @@ package android.widget.listview; -import android.view.View; -import android.view.ViewGroup; import android.util.ListItemFactory; import android.util.ListScenario; +import android.view.View; +import android.view.ViewGroup; /** * A list where each item is tall with buttons that are farther apart than the screen diff --git a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java index ea2c5f2caf13..2a0e013be616 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java @@ -17,13 +17,13 @@ package android.widget.listview; import android.content.Context; +import android.util.InternalSelectionView; +import android.util.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; -import android.util.InternalSelectionView; -import android.util.ListScenario; /** * Each item is an internal selection view, a button, and some filler diff --git a/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java b/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java index 73eb0a8f8e63..91ff06b3f670 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java @@ -18,13 +18,13 @@ package android.widget.listview; import android.graphics.Rect; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; -import android.view.View; import android.view.KeyEvent; +import android.view.View; import android.widget.ListView; -import android.widget.listview.ListOfThinItems; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; public class ListItemRequestRectAboveThinFirstItemTest extends ActivityInstrumentationTestCase<ListOfThinItems> { diff --git a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java index a5fe17ac7603..d80fd90de53e 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java @@ -17,13 +17,12 @@ package android.widget.listview; import android.content.Context; +import android.util.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.TextView; -import android.util.ListScenario; - /** * A list where each item expands by 1.5 when selected. */ diff --git a/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java index 12b5ef4067ed..54f302c8178c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java +++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java @@ -20,14 +20,13 @@ import android.app.ListActivity; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; -import android.provider.Settings; import android.provider.Contacts.People; +import android.provider.Settings; import android.view.View; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter; -import android.widget.AdapterView.OnItemClickListener; - public class ListManagedCursor extends ListActivity implements OnItemClickListener { diff --git a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java index bc3776cfb02e..8e0b6fe7ef55 100644 --- a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java @@ -18,11 +18,12 @@ package android.widget.listview; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; +import android.test.TouchUtils; import android.view.KeyEvent; import android.widget.ListView; -import android.test.TouchUtils; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; /** * Tests restoring the scroll position in a list with a managed cursor. diff --git a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java index 919ef69fc8a2..70b9081128a0 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java @@ -16,12 +16,11 @@ package android.widget.listview; +import android.util.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.util.ListScenario; - /** * Each list item has two focusables that are close enough together that * it shouldn't require panning to move focus. diff --git a/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java b/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java index 76814fb480cd..075712efe64a 100644 --- a/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java +++ b/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java @@ -18,11 +18,11 @@ package android.widget.listview; import android.app.Activity; import android.os.Bundle; +import android.view.View; +import android.view.ViewDebug; import android.widget.ArrayAdapter; -import android.widget.ListView; import android.widget.ImageButton; -import android.view.ViewDebug; -import android.view.View; +import android.widget.ListView; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java b/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java index 896bd19d311f..be14de8986d8 100644 --- a/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java @@ -16,12 +16,11 @@ package android.widget.listview; -import android.widget.listview.ListItemFocusablesClose; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; +import androidx.test.filters.MediumTest; + public class ListRetainsFocusAcrossLayoutsTest extends ActivityInstrumentationTestCase<ListItemFocusablesClose> { public ListRetainsFocusAcrossLayoutsTest() { diff --git a/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java b/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java index 7b29a66c22fa..28addd6431ae 100644 --- a/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java @@ -19,12 +19,13 @@ package android.widget.listview; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.AbsListView; import android.widget.ListView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class ListScrollListenerTest extends ActivityInstrumentationTestCase<ListScrollListener> implements AbsListView.OnScrollListener { private ListScrollListener mActivity; diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java index 6c2e26401eed..af8e899a867a 100644 --- a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java +++ b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java @@ -16,12 +16,12 @@ package android.widget.listview; +import android.os.Bundle; import android.util.ListScenario; import android.view.KeyEvent; import android.view.View; -import android.os.Bundle; -import android.widget.LinearLayout; import android.widget.Button; +import android.widget.LinearLayout; /** * List of 1,000 items used to test calls to setSelection() in touch mode. diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java b/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java index 4cef16470609..2caca1309d0c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java @@ -17,10 +17,11 @@ package android.widget.listview; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.ListView; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.MediumTest; + /** * Basic tests of setting & clearing the selection */ diff --git a/core/tests/coretests/src/android/widget/listview/ListSimple.java b/core/tests/coretests/src/android/widget/listview/ListSimple.java index 6accae1c3ef1..f53638eb8ff1 100644 --- a/core/tests/coretests/src/android/widget/listview/ListSimple.java +++ b/core/tests/coretests/src/android/widget/listview/ListSimple.java @@ -16,11 +16,10 @@ package android.widget.listview; +import android.os.Bundle; import android.util.ListScenario; - import android.view.View; import android.view.ViewGroup; -import android.os.Bundle; import android.widget.TextView; public class ListSimple extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java b/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java index 95f09f6e78bc..c4e9fe941b72 100644 --- a/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java +++ b/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java @@ -16,17 +16,17 @@ package android.widget.listview; -import com.android.frameworks.coretests.R; - import android.app.ListActivity; import android.content.Context; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.widget.BaseAdapter; import android.widget.TextView; +import com.android.frameworks.coretests.R; + /** * Exercises moving focus into the list from the side */ diff --git a/core/tests/coretests/src/android/widget/listview/ListThrasher.java b/core/tests/coretests/src/android/widget/listview/ListThrasher.java index 0237a9551ce5..d82f68d11c92 100644 --- a/core/tests/coretests/src/android/widget/listview/ListThrasher.java +++ b/core/tests/coretests/src/android/widget/listview/ListThrasher.java @@ -16,19 +16,19 @@ package android.widget.listview; -import com.android.frameworks.coretests.R; - import android.app.ListActivity; import android.content.Context; import android.os.Bundle; import android.os.Handler; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.TextView; +import com.android.frameworks.coretests.R; + import java.util.Random; /** diff --git a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java index 986cc5713db0..31339e9ee9c2 100644 --- a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java +++ b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java @@ -16,8 +16,6 @@ package android.widget.listview; -import android.view.Gravity; - import android.util.ListScenario; /** diff --git a/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java b/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java index 199d069e16ef..0fc87ea063ed 100644 --- a/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java +++ b/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java @@ -16,13 +16,14 @@ package android.widget.listview; -import com.android.frameworks.coretests.R; - -import android.test.ActivityInstrumentationTestCase; import android.app.Activity; -import android.test.suitebuilder.annotation.MediumTest; +import android.test.ActivityInstrumentationTestCase; import android.widget.ListView; +import androidx.test.filters.MediumTest; + +import com.android.frameworks.coretests.R; + public class ListUnspecifiedMeasure<T extends Activity> extends ActivityInstrumentationTestCase<T> { private T mActivity; private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java b/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java index 5ab2757deae7..63941f1c967d 100644 --- a/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java +++ b/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java @@ -18,12 +18,12 @@ package android.widget.listview; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.Button; import android.widget.ListView; +import androidx.test.filters.MediumTest; + import com.android.frameworks.coretests.R; -import android.widget.listview.ListViewHeight; public class ListViewHeightTest extends ActivityInstrumentationTestCase<ListViewHeight> { private ListViewHeight mActivity; diff --git a/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java b/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java index 348ea1b75578..10ba8b73cd02 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java @@ -16,8 +16,6 @@ package android.widget.listview; -import com.android.frameworks.coretests.R; - import android.app.ListActivity; import android.database.Cursor; import android.os.Bundle; @@ -32,6 +30,8 @@ import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.Toast; +import com.android.frameworks.coretests.R; + /** * See 1080989. You need some contacts for this adapter. */ diff --git a/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java b/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java index 74dd06c1ba71..52273fdc98a0 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java @@ -16,8 +16,6 @@ package android.widget.listview; -import com.android.frameworks.coretests.R; - import android.app.ListActivity; import android.content.Context; import android.os.Bundle; @@ -26,6 +24,8 @@ import android.view.MenuItem; import android.view.View; import android.widget.ArrayAdapter; +import com.android.frameworks.coretests.R; + /** * Tests using an empty view with a list */ diff --git a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java index aea091ae9cf3..6030582a2445 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java @@ -16,9 +16,8 @@ package android.widget.listview; -import android.util.ListScenario; - import android.os.Bundle; +import android.util.ListScenario; import android.widget.Button; import android.widget.ListAdapter; import android.widget.ListView; diff --git a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java index 26e1d5d88e1e..13e770cd4e47 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java @@ -16,8 +16,8 @@ package android.widget.listview; -import android.widget.TextView; import android.util.ListScenario; +import android.widget.TextView; /** * The header text view echos the value of the selected item by using (indirectly) diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java index ec8ab7e77357..22c28c29b322 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java @@ -17,13 +17,14 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; import android.util.ListUtil; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; import android.widget.listview.ListInterleaveFocusables; +import androidx.test.filters.MediumTest; + public class ListInterleaveFocusablesTest extends ActivityInstrumentationTestCase2<ListInterleaveFocusables> { private ListView mListView; private ListUtil mListUtil; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java index 82f48801111e..c645b7c5b767 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java @@ -17,11 +17,12 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; import android.view.KeyEvent; +import android.widget.ListView; import android.widget.listview.ListItemFocusableAboveUnfocusable; +import androidx.test.filters.MediumTest; + public class ListItemFocusableAboveUnfocusableTest extends ActivityInstrumentationTestCase<ListItemFocusableAboveUnfocusable> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java index 3b30ebe766cb..c7525b33d6de 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java @@ -17,12 +17,13 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.listview.ListItemFocusablesClose; +import androidx.test.filters.MediumTest; + public class ListItemFocusablesCloseTest extends ActivityInstrumentationTestCase<ListItemFocusablesClose> { private ListView mListView; private int mListTop; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java index 475930df37e3..4bb2206984da 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java @@ -17,7 +17,6 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -25,6 +24,8 @@ import android.widget.Button; import android.widget.ListView; import android.widget.listview.ListItemFocusablesFarApart; +import androidx.test.filters.MediumTest; + public class ListItemFocusablesFarApartTest extends ActivityInstrumentationTestCase<ListItemFocusablesFarApart> { private ListView mListView; private int mListTop; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java index 91a1eba124de..76052914e027 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java @@ -17,13 +17,14 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.ListView; import android.view.KeyEvent; +import android.widget.ListView; import android.widget.listview.ListItemsExpandOnSelection; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListItemsExpandOnSelectionTest extends ActivityInstrumentationTestCase<ListItemsExpandOnSelection> { private ListView mListView; private int mListTop; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java index 5bc121a907ae..fdae4833532a 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java @@ -17,12 +17,13 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; -import android.view.View; import android.view.KeyEvent; +import android.view.View; +import android.widget.ListView; import android.widget.listview.ListLastItemPartiallyVisible; +import androidx.test.filters.MediumTest; + public class ListLastItemPartiallyVisibleTest extends ActivityInstrumentationTestCase<ListLastItemPartiallyVisible> { private ListView mListView; private int mListBottom; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java index bda71d0d3fe6..d44b130164f2 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java @@ -17,14 +17,15 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; import android.widget.TextView; import android.widget.listview.ListOfItemsShorterThanScreen; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListOfItemsShorterThanScreenTest extends ActivityInstrumentationTestCase<ListOfItemsShorterThanScreen> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java index 213544526170..1decdadd7fb8 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java @@ -17,13 +17,14 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; import android.widget.listview.ListOfItemsTallerThanScreen; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListOfItemsTallerThanScreenTest extends ActivityInstrumentationTestCase2<ListOfItemsTallerThanScreen> { diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java index ef70b5ae46a5..67808f1945f9 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java @@ -17,13 +17,14 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.ListUtil; import android.view.KeyEvent; import android.widget.ListView; import android.widget.listview.ListOfShortShortTallShortShort; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationTestCase2<ListOfShortShortTallShortShort> { private ListView mListView; private ListUtil mListUtil; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java index c958591e8c2e..f9aa6dc13b6c 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java @@ -17,11 +17,12 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; import android.view.KeyEvent; +import android.widget.ListView; import android.widget.listview.ListOfShortTallShort; +import androidx.test.filters.MediumTest; + public class ListOfShortTallShortTest extends ActivityInstrumentationTestCase<ListOfShortTallShort> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java index c191d71aaf6e..6bd1cb5557a6 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java @@ -17,14 +17,15 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; import android.widget.listview.ListOfThinItems; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListOfThinItemsTest extends ActivityInstrumentationTestCase<ListOfThinItems> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java index 9a8e63421d61..60bc1155bc00 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java @@ -16,12 +16,13 @@ package android.widget.listview.arrowscroll; -import android.support.test.filters.LargeTest; import android.test.ActivityInstrumentationTestCase2; import android.view.KeyEvent; +import android.widget.AdapterView; import android.widget.ListView; import android.widget.listview.ListWithFirstScreenUnSelectable; -import android.widget.AdapterView; + +import androidx.test.filters.LargeTest; @LargeTest public class ListWithFirstScreenUnSelectableTest diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java index 56ca00987dc2..a60054e2e821 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java @@ -17,12 +17,13 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.ListView; import android.widget.listview.ListWithNoFadingEdge; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + public class ListWithNoFadingEdgeTest extends ActivityInstrumentationTestCase<ListWithNoFadingEdge> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java index cf319d11ea88..80e70c6e4cac 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java @@ -17,14 +17,15 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; import android.widget.TextView; import android.widget.listview.ListWithOffScreenNextSelectable; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + @Suppress // Failing. public class ListWithOffScreenNextSelectableTest extends ActivityInstrumentationTestCase<ListWithOffScreenNextSelectable> { diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java index feea9b2c403c..819b739fdd32 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java @@ -17,12 +17,13 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; +import android.view.KeyEvent; import android.widget.ListView; import android.widget.TextView; -import android.view.KeyEvent; import android.widget.listview.ListWithOnItemSelectedAction; +import androidx.test.filters.MediumTest; + public class ListWithOnItemSelectedActionTest extends ActivityInstrumentationTestCase<ListWithOnItemSelectedAction> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java index 211c8c8d0f7e..9bbdc2ae8b9e 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java @@ -17,13 +17,14 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; import android.widget.listview.ListWithScreenOfNoSelectables; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTestCase2<ListWithScreenOfNoSelectables> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java index 42058f030020..21fa51d4ae13 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java @@ -17,11 +17,12 @@ package android.widget.listview.arrowscroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; import android.view.KeyEvent; +import android.widget.ListView; import android.widget.listview.ListWithSeparators; +import androidx.test.filters.MediumTest; + public class ListWithSeparatorsTest extends ActivityInstrumentationTestCase<ListWithSeparators> { private ListWithSeparators mActivity; private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java index 6a7466b627aa..e9baabf3175a 100644 --- a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java +++ b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java @@ -16,14 +16,14 @@ package android.widget.listview.focus; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.listview.AdjacentListsWithAdjacentISVsInside; -import android.util.InternalSelectionView; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; +import android.util.InternalSelectionView; import android.view.KeyEvent; import android.widget.ListView; +import android.widget.listview.AdjacentListsWithAdjacentISVsInside; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; public class AdjacentListsWithAdjacentISVsInsideTest extends ActivityInstrumentationTestCase<AdjacentListsWithAdjacentISVsInside> { diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java index 5540d650dd19..3fbdacc302f5 100644 --- a/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java +++ b/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java @@ -16,15 +16,15 @@ package android.widget.listview.focus; -import android.widget.listview.ListButtonsDiagonalAcrossItems; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.FocusFinder; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.ListView; +import android.widget.listview.ListButtonsDiagonalAcrossItems; + +import androidx.test.filters.MediumTest; /** * Test that ListView will override default behavior of focus searching to diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java index edc60b58e9d0..544895279e6d 100644 --- a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java +++ b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java @@ -17,12 +17,13 @@ package android.widget.listview.focus; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.Button; import android.widget.ListView; import android.widget.listview.ListHorizontalFocusWithinItemWins; +import androidx.test.filters.MediumTest; + public class ListHorizontalFocusWithinItemWinsTest extends ActivityInstrumentationTestCase<ListHorizontalFocusWithinItemWins> { private ListView mListView; diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java index b449b61baf83..0b17aaf4792c 100644 --- a/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java +++ b/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java @@ -17,17 +17,18 @@ package android.widget.listview.focus; import android.test.ActivityInstrumentationTestCase2; -import android.test.FlakyTest; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.view.View; import android.widget.AbsListView; import android.widget.ListView; import android.widget.listview.ListWithEditTextHeader; +import androidx.test.filters.FlakyTest; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + public class ListWithEditTextHeaderTest extends ActivityInstrumentationTestCase2<ListWithEditTextHeader> { private ListView mListView; @@ -49,7 +50,7 @@ public class ListWithEditTextHeaderTest extends ActivityInstrumentationTestCase2 assertTrue("header does not have focus", mListView.getChildAt(0).isFocused()); } - @FlakyTest(tolerance=2) + @FlakyTest @LargeTest public void testClickingHeaderKeepsFocus() { TouchUtils.clickView(this, mListView.getChildAt(0)); diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java index 28f899edc9b6..262d0f4ba42f 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java @@ -17,14 +17,14 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; -import android.widget.ListView; import android.view.View; - +import android.widget.ListView; import android.widget.listview.ListGetSelectedView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + /** * This test is made to check that getSelectedView() will return * null in touch mode. diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java index ffa9a5e79543..48e8c5e24946 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java @@ -17,15 +17,14 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; +import android.test.TouchUtils; import android.view.Gravity; import android.view.View; import android.view.ViewConfiguration; import android.widget.ListView; - import android.widget.listview.ListOfTouchables; -import android.test.TouchUtils; + +import androidx.test.filters.MediumTest; /** * Touch tests for a list where all of the items fit on the screen. diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java index aed513a0721b..716683ab52ad 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java @@ -17,15 +17,15 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.Suppress; import android.view.View; import android.widget.ListView; - import android.widget.listview.ListSimple; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + /** * Tests setting the selection in touch mode */ diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java index 7daf64e0a596..6a1f076ea2c3 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java @@ -17,16 +17,16 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.Gravity; import android.view.View; import android.view.ViewConfiguration; import android.widget.ListView; - import android.widget.listview.ListBottomGravityMany; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + /** * Touch tests for a list where all of the items do not fit on the screen, and the list * stacks from the bottom. diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java index 4086cf094ab9..89498d6632b9 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java @@ -17,13 +17,13 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.View; import android.widget.ListView; - import android.widget.listview.ListBottomGravity; +import androidx.test.filters.MediumTest; + /** * Touch tests for a list where all of the items fit on the screen, and the list * stacks from the bottom. diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java index 30d56caee7f6..c7f23714f3ba 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java @@ -17,16 +17,16 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.Gravity; import android.view.View; import android.view.ViewConfiguration; import android.widget.ListView; - import android.widget.listview.ListTopGravityMany; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + /** * Touch tests for a list where all of the items do not fit on the screen. */ diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java index 5b064b3e770d..8a557aba700e 100644 --- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java +++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java @@ -17,13 +17,13 @@ package android.widget.listview.touch; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.TouchUtils; import android.view.View; import android.widget.ListView; - import android.widget.listview.ListTopGravity; +import androidx.test.filters.MediumTest; + /** * Touch tests for a list where all of the items fit on the screen. */ diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java index a7f5c0560a4e..a30985bfaf87 100644 --- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java +++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java @@ -18,7 +18,6 @@ package android.widget.scroll; import android.util.InternalSelectionView; import android.util.ScrollViewScenario; - import android.widget.Button; /** diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java index 8123228c5228..825aa1aa85a3 100644 --- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java +++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java @@ -16,14 +16,13 @@ package android.widget.scroll; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.scroll.ButtonAboveTallInternalSelectionView; -import android.util.InternalSelectionView; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; +import android.util.InternalSelectionView; import android.view.KeyEvent; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + @Suppress // Failing. public class ButtonAboveTallInternalSelectionViewTest extends ActivityInstrumentationTestCase<ButtonAboveTallInternalSelectionView> { diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java index 3d5f86d35b07..47d36ddf9be1 100644 --- a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java +++ b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java @@ -17,7 +17,6 @@ package android.widget.scroll; import android.util.ScrollViewScenario; - import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java index afc275f25181..dba07a086e89 100644 --- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java +++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java @@ -16,15 +16,15 @@ package android.widget.scroll; -import com.android.frameworks.coretests.R; - import android.app.Activity; -import android.os.Bundle; import android.graphics.Rect; +import android.os.Bundle; import android.view.View; import android.widget.ScrollView; import android.widget.TextView; +import com.android.frameworks.coretests.R; + /** * A screen with some scenarios that exercise {@link ScrollView}'s implementation * of {@link android.view.ViewGroup#requestChildRectangleOnScreen}: diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java index f8abdb2c8ec1..7c3df910a547 100644 --- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java +++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java @@ -16,19 +16,19 @@ package android.widget.scroll; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.scroll.RequestRectangleVisible; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.test.ViewAsserts; +import android.view.KeyEvent; +import android.view.View; import android.widget.Button; import android.widget.ScrollView; import android.widget.TextView; -import android.view.View; -import android.view.KeyEvent; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; + +import com.android.frameworks.coretests.R; /** * {@link RequestRectangleVisible} is set up to exercise the cases of moving a diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java index 731b25a08926..105686ad7a92 100644 --- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java +++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java @@ -16,15 +16,14 @@ package android.widget.scroll; -import com.android.frameworks.coretests.R; - import android.app.Activity; +import android.graphics.Rect; import android.os.Bundle; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Button; import android.view.View; -import android.graphics.Rect; +import android.widget.Button; +import android.widget.TextView; + +import com.android.frameworks.coretests.R; public class RequestRectangleVisibleWithInternalScroll extends Activity { diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java index 5e9b520a9ec2..1a221ff63863 100644 --- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java +++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java @@ -16,16 +16,17 @@ package android.widget.scroll; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase; import android.test.ViewAsserts; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.widget.Button; import android.widget.ScrollView; import android.widget.TextView; +import androidx.test.filters.Suppress; + +import com.android.frameworks.coretests.R; + /** * This is suppressed because {@link TextView#scrollBy} isn't working. */ diff --git a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java index 027ea0f83350..92a315296064 100644 --- a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java +++ b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java @@ -16,15 +16,14 @@ package android.widget.scroll; -import com.android.frameworks.coretests.R; - import android.app.Activity; import android.os.Bundle; +import android.widget.Button; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; -import android.widget.Button; +import com.android.frameworks.coretests.R; /** * Basic scroll view example diff --git a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java index 3fd17ee51abf..8d71f84d8f7d 100644 --- a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java +++ b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java @@ -17,14 +17,14 @@ package android.widget.scroll; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; import android.view.KeyEvent; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ScrollView; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; public class ScrollViewButtonsAndLabelsTest extends ActivityInstrumentationTestCase<ScrollViewButtonsAndLabels> { diff --git a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java index 3a0f29a530ee..90ede7d35511 100644 --- a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java +++ b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java @@ -17,7 +17,6 @@ package android.widget.scroll; import android.util.ScrollViewScenario; - import android.widget.Button; import android.widget.LinearLayout; diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java index 56d7ed2c3243..04f5ac84bd2a 100644 --- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java +++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java @@ -16,16 +16,16 @@ package android.widget.scroll.arrowscroll; -import android.widget.scroll.ButtonsWithTallTextViewInBetween; - import android.graphics.Rect; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.ScrollView; import android.widget.TextView; +import android.widget.scroll.ButtonsWithTallTextViewInBetween; + +import androidx.test.filters.MediumTest; public class ButtonsWithTallTextViewInBetweenTest extends ActivityInstrumentationTestCase<ButtonsWithTallTextViewInBetween> { diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java index 6ce4c1508213..2f4509827592 100644 --- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java +++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java @@ -16,14 +16,14 @@ package android.widget.scroll.arrowscroll; -import android.test.suitebuilder.annotation.Suppress; -import android.widget.scroll.arrowscroll.MultiPageTextWithPadding; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; -import android.widget.TextView; import android.widget.ScrollView; +import android.widget.TextView; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.Suppress; @Suppress // Flaky public class MultiPageTextWithPaddingTest extends diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java index 267d8ee4a250..a2928cb78e0c 100644 --- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java +++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java @@ -16,15 +16,15 @@ package android.widget.scroll.arrowscroll; -import android.widget.scroll.ShortButtons; - import android.graphics.Rect; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.Button; import android.widget.ScrollView; +import android.widget.scroll.ShortButtons; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; public class ShortButtonsTest extends ActivityInstrumentationTestCase<ShortButtons> { diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java index 5351839e868f..0681081f7452 100644 --- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java +++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java @@ -16,13 +16,13 @@ package android.widget.scroll.arrowscroll; -import android.widget.scroll.TallTextAboveButton; - import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.ScrollView; import android.widget.TextView; +import android.widget.scroll.TallTextAboveButton; + +import androidx.test.filters.MediumTest; public class TallTextAboveButtonTest extends ActivityInstrumentationTestCase<TallTextAboveButton> { private ScrollView mScrollView; diff --git a/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java b/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java index 449c95cc81fe..aa20c9b65f2e 100644 --- a/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java +++ b/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java @@ -16,16 +16,17 @@ package android.widget.touchmode; -import android.widget.layout.linear.LLOfButtons1; -import android.widget.layout.linear.LLOfButtons2; import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick; -import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey; import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap; +import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; +import android.widget.layout.linear.LLOfButtons1; +import android.widget.layout.linear.LLOfButtons2; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; /** * Tests that the touch mode changes from various events, and that the state diff --git a/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java b/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java index 691b25a51b8a..97c982f3cbfd 100644 --- a/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java +++ b/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java @@ -16,12 +16,12 @@ package android.widget.touchmode; -import android.widget.layout.linear.LLOfTwoFocusableInTouchMode; - import android.test.ActivityInstrumentationTestCase2; import android.test.TouchUtils; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; +import android.widget.layout.linear.LLOfTwoFocusableInTouchMode; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; public class FocusableInTouchModeClickTest extends ActivityInstrumentationTestCase2<LLOfTwoFocusableInTouchMode> { diff --git a/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java b/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java index 5339188ae89a..88aa9caa31b6 100644 --- a/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java +++ b/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java @@ -16,14 +16,15 @@ package android.widget.touchmode; -import android.widget.layout.linear.LLEditTextThenButton; import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey; import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.Button; import android.widget.EditText; +import android.widget.layout.linear.LLEditTextThenButton; + +import androidx.test.filters.MediumTest; public class StartInTouchWithViewInFocusTest extends ActivityInstrumentationTestCase2<LLEditTextThenButton> { diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java index 5a6110c08116..f192f6815163 100644 --- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java +++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java @@ -21,11 +21,11 @@ import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap; import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.Button; import android.widget.layout.linear.LLOfButtons1; +import androidx.test.filters.MediumTest; /** * Make sure focus isn't kept by buttons when entering touch mode. diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java index 3ddeef072a9e..87f33a4cc803 100644 --- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java +++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java @@ -20,12 +20,13 @@ import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick; import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap; import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; import android.widget.Button; import android.widget.EditText; import android.widget.layout.linear.LLEditTextThenButton; +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; + /** * Some views, like edit texts, can keep and gain focus even when in touch mode. */ diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index a302657c7ede..aadfcbc84f76 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -57,7 +57,6 @@ import android.os.Vibrator; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.speech.tts.Voice; -import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; import android.text.TextUtils; import android.view.Window; @@ -66,6 +65,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.Toast; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider; import com.android.internal.util.test.FakeSettingsProvider; 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 1859378e4c1f..aaa624e6b25e 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -16,41 +16,41 @@ package com.android.internal.app; -import com.android.internal.R; -import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; - -import android.app.usage.UsageStats; -import android.app.usage.UsageStatsManager; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.os.UserHandle; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; + import static com.android.internal.app.ChooserWrapperActivity.sOverrides; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.usage.UsageStatsManager; +import android.content.Intent; +import android.content.pm.ResolveInfo; + +import androidx.test.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; /** * Chooser activity instrumentation tests 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 c446f3c79ea8..60529f6d4071 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -16,14 +16,14 @@ package com.android.internal.app; +import static org.mockito.Mockito.mock; + import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.pm.PackageManager; import java.util.function.Function; -import static org.mockito.Mockito.mock; - public class ChooserWrapperActivity extends ChooserActivity { /* * Simple wrapper around chooser activity to be able to initiate it under test diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java index cfb6bdd5d613..9b13af2a4357 100644 --- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java @@ -46,9 +46,10 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Rule; diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 404c99c82a4c..fe2fb8530cf6 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -16,6 +16,29 @@ package com.android.internal.app; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +import static com.android.internal.app.ResolverWrapperActivity.sOverrides; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.view.View; +import android.widget.RelativeLayout; + +import androidx.test.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import com.android.internal.widget.ResolverDrawerLayout; @@ -26,37 +49,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -import android.app.usage.UsageStats; -import android.app.usage.UsageStatsManager; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.os.UserHandle; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; -import android.view.View; -import android.widget.RelativeLayout; - import java.util.ArrayList; import java.util.List; -import java.util.Map; - -import static android.os.SystemClock.sleep; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static com.android.internal.app.ResolverWrapperActivity.sOverrides; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * Resolver activity instrumentation tests diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java index c710b9ad77bf..850b466ec755 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java @@ -23,10 +23,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.os.UserHandle; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - /** * Utility class used by resolver tests to create mock data */ diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java index 284ab605c3b5..fcec00e34278 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java @@ -25,34 +25,31 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.mockito.invocation.InvocationOnMock; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - import android.app.usage.IUsageStatsManager; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.content.Intent; import android.os.RemoteException; import android.os.UserHandle; -import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; -import java.io.File; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java index 163211e10e6a..83f6bc2e8bf3 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java @@ -16,18 +16,13 @@ package com.android.internal.app; +import static org.mockito.Mockito.mock; + import android.app.usage.UsageStatsManager; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.os.RemoteException; import java.util.function.Function; -import static org.mockito.Mockito.mock; - - /* * Simple wrapper around chooser activity to be able to initiate it under test */ diff --git a/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java b/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java index 472958a01eb4..87ad124fe04b 100644 --- a/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java +++ b/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java @@ -17,12 +17,13 @@ package com.android.internal.app; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.SmallTest; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; + /** * Tests for {@link WindowDecorActionBar}. */ diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java index 115af5e0ea5d..f7fea3b7a6d0 100644 --- a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java +++ b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java @@ -16,20 +16,14 @@ package com.android.internal.app.procstats; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; - -import android.os.BatteryStats; import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; +import androidx.test.filters.SmallTest; + import junit.framework.Assert; import junit.framework.TestCase; -import org.mockito.Mockito; - /** * Provides test cases for SparseMappingTable. */ diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java index 629f7b60471c..222e494de7d4 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java @@ -18,10 +18,11 @@ package com.android.internal.inputmethod; import static org.junit.Assert.assertEquals; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.WindowManager.LayoutParams; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java index 8179328c32c4..ba6390808151 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java @@ -18,8 +18,8 @@ package com.android.internal.inputmethod; import static org.junit.Assert.assertEquals; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java index 992b46f96339..b6878013b254 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import static android.os.BatteryStats.STATS_SINCE_CHARGED; @@ -20,10 +21,11 @@ import static android.os.BatteryStats.STATS_SINCE_CHARGED; import android.app.ActivityManager; import android.os.BatteryStats; import android.os.WorkSource; -import android.support.test.filters.SmallTest; import android.util.ArrayMap; import android.view.Display; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; /** diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java index 08f8dd146516..37f818a70ec9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java @@ -13,11 +13,13 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import android.os.BatteryStats; import android.os.Parcel; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java index ee8d5081b0b3..0179eadae6a1 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static android.os.BatteryStats.STATS_SINCE_CHARGED; @@ -36,11 +37,12 @@ import static org.mockito.Mockito.when; import android.os.BatteryStats; import android.os.UserHandle; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseLongArray; import android.view.Display; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.util.ArrayUtils; import org.junit.Before; @@ -64,7 +66,7 @@ import java.util.Arrays; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner * * or * diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java index 3a5a9f5bc67a..efb871027830 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java @@ -13,10 +13,12 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import android.os.BatteryStats; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java index 19dab7961401..a42286f6882f 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java @@ -13,11 +13,13 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import android.os.BatteryStats; import android.os.Parcel; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index 7467114a7596..355601ca05ed 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -17,7 +17,6 @@ package com.android.internal.os; - import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; import static com.google.common.truth.Truth.assertThat; @@ -37,12 +36,13 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.BatteryStats; import android.os.Process; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.format.DateUtils; import android.util.StatsLog; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import junit.framework.TestCase; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java index 10a318920646..cc0ddb7bf8a4 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; - import android.content.Context; import android.os.Parcel; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java index 613de45173d6..dc9367557f9f 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static android.os.BatteryStats.STATS_SINCE_CHARGED; @@ -31,12 +32,13 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import android.os.BatteryStats; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.Display; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.util.ArrayUtils; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index 8cbe5d68d554..3e33273feddb 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import static android.os.BatteryStats.STATS_CURRENT; @@ -20,21 +21,20 @@ import static android.os.BatteryStats.STATS_SINCE_CHARGED; import static android.os.BatteryStats.WAKE_TYPE_PARTIAL; import android.app.ActivityManager; -import android.os.BatteryManager; import android.os.BatteryStats; import android.os.BatteryStats.HistoryItem; import android.os.BatteryStats.Uid.Sensor; import android.os.WorkSource; -import android.support.test.filters.SmallTest; import android.view.Display; +import androidx.test.filters.SmallTest; + import com.android.internal.os.BatteryStatsImpl.DualTimer; import com.android.internal.os.BatteryStatsImpl.Uid; + import junit.framework.TestCase; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -47,7 +47,7 @@ import java.util.Map; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsNoteTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ public class BatteryStatsNoteTest extends TestCase { diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java index 251ceb04b973..61d20dfb32d9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java @@ -13,11 +13,13 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import android.os.BatteryStats; import android.os.Parcel; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java index a751f9060588..b851f0ad3414 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java @@ -13,13 +13,15 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import android.app.ActivityManager; import android.os.BatteryStats; -import android.support.test.filters.SmallTest; import android.view.Display; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; /** diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java index 5fd822549ff8..b9995c443f35 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java @@ -18,7 +18,8 @@ package com.android.internal.os; import android.os.BatteryStats; import android.os.Parcel; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.Assert; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java index 015314ebe8dc..f76f31619f79 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java @@ -13,10 +13,12 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; import android.os.BatteryStats; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 225515e9e3f3..d69e1d131731 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java index 3190d9e77c77..bce8b40cc31d 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java @@ -16,19 +16,20 @@ package com.android.internal.os; -import java.io.PrintWriter; -import java.io.StringWriter; - import android.os.BatteryStats; import android.os.Parcel; -import android.support.test.filters.SmallTest; import android.util.Log; +import androidx.test.filters.SmallTest; + import junit.framework.Assert; import junit.framework.TestCase; import org.mockito.Mockito; +import java.io.PrintWriter; +import java.io.StringWriter; + /** * Provides test cases for android.os.BatteryStats. */ diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java index 98d0f7fdce5c..87dc2f3dd43b 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java @@ -18,16 +18,17 @@ package com.android.internal.os; import android.os.BatteryStats; import android.os.Parcel; -import android.support.test.filters.SmallTest; import android.util.StringBuilderPrinter; -import junit.framework.Assert; -import junit.framework.TestCase; +import androidx.test.filters.SmallTest; import com.android.internal.os.BatteryStatsImpl.Clocks; import com.android.internal.os.BatteryStatsImpl.TimeBase; import com.android.internal.os.BatteryStatsImpl.Timer; +import junit.framework.Assert; +import junit.framework.TestCase; + /** * Provides test cases for android.os.BatteryStats. */ diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java index a7e75a248eda..4df3190cc2d7 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java @@ -16,22 +16,10 @@ package com.android.internal.os; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; +import androidx.test.filters.SmallTest; -import android.os.BatteryStats; -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import junit.framework.Assert; import junit.framework.TestCase; -import com.android.internal.os.BatteryStatsImpl; - -import org.mockito.Mockito; - /** * Provides test cases for android.os.BatteryStats. */ diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java index 450473d0335c..e7a1bcae459a 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java @@ -28,12 +28,13 @@ import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.util.ArraySet; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index dc3a12f0bbf5..1d35143e3fab 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -22,11 +22,12 @@ import static org.junit.Assert.assertTrue; import android.os.Binder; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; import android.util.SparseArray; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.os.BinderInternal.CallSession; import org.junit.Assert; diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java index 42c9139e04e2..01515bd9c6b5 100644 --- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static android.os.BatteryStats.UID_TIMES_TYPE_ALL; @@ -33,9 +34,6 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; -import com.android.frameworks.coretests.aidl.ICmdCallback; -import com.android.frameworks.coretests.aidl.ICmdReceiver; - import android.app.ActivityManager; import android.app.KeyguardManager; import android.content.ComponentName; @@ -52,14 +50,18 @@ import android.os.PowerManager; import android.os.Process; import android.os.SystemClock; import android.provider.Settings; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.text.TextUtils; import android.util.DebugUtils; import android.util.Log; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.coretests.aidl.ICmdCallback; +import com.android.frameworks.coretests.aidl.ICmdReceiver; + import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; diff --git a/core/tests/coretests/src/com/android/internal/os/DebugTest.java b/core/tests/coretests/src/com/android/internal/os/DebugTest.java index efb78d788575..2a8a8571c882 100644 --- a/core/tests/coretests/src/com/android/internal/os/DebugTest.java +++ b/core/tests/coretests/src/com/android/internal/os/DebugTest.java @@ -18,7 +18,8 @@ package com.android.internal.os; import android.os.Debug; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; @SmallTest diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java index 8360126f3751..a25a74890029 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java @@ -24,9 +24,10 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java index 2663f2bc8ae1..7a316056429a 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java @@ -24,9 +24,10 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java new file mode 100644 index 000000000000..c03d1f37ffa7 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java @@ -0,0 +1,214 @@ +/* + * 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.internal.os; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.os.SystemClock; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.internal.os.KernelCpuThreadReader.ProcessCpuUsage; +import com.android.internal.os.KernelCpuThreadReader.ThreadCpuUsage; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.OptionalDouble; +import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * End to end test for {@link KernelCpuThreadReader} that checks the accuracy of the reported times + * by spawning threads that do a predictable amount of work + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class KernelCpuThreadReaderEndToEndTest { + + private static final int TIMED_NUM_SAMPLES = 5; + private static final int TIMED_START_MILLIS = 500; + private static final int TIMED_END_MILLIS = 2000; + private static final int TIMED_INCREMENT_MILLIS = 500; + private static final int TIMED_COMPARISON_DELTA_MILLIS = 200; + + private static final int ITERATIVE_NUM_SAMPLES = 100; + private static final long ITERATIVE_LOW_ITERATIONS = (long) 1e8; + private static final long ITERATIVE_HIGH_ITERATIONS = (long) 2e8; + private static final double ITERATIONS_COMPARISONS_DELTA = 0.25; + + /** + * Test that when we busy-wait for the thread-local time to reach N seconds, the time reported + * is also N seconds. Takes ~10s. + */ + @Test + public void testTimedWork() throws InterruptedException { + for (int millis = TIMED_START_MILLIS; + millis <= TIMED_END_MILLIS; + millis += TIMED_INCREMENT_MILLIS) { + final Duration targetDuration = Duration.ofMillis(millis); + final Runnable work = timedWork(targetDuration); + Duration resultDuration = getAverageWorkTime( + work, String.format("timed%dms", millis), TIMED_NUM_SAMPLES); + assertEquals( + "Time worked according to currentThreadTimeMillis doesn't match " + + "KernelCpuThreadReader", + targetDuration.toMillis(), resultDuration.toMillis(), + TIMED_COMPARISON_DELTA_MILLIS); + } + } + + /** + * Test that when we scale up the amount of work by N, the time reported also scales by N. Takes + * ~15s. + */ + @Test + public void testIterativeWork() throws InterruptedException { + final Runnable lowAmountWork = iterativeWork(ITERATIVE_LOW_ITERATIONS); + final Runnable highAmountWork = iterativeWork(ITERATIVE_HIGH_ITERATIONS); + final Duration lowResultDuration = + getAverageWorkTime(lowAmountWork, "iterlow", ITERATIVE_NUM_SAMPLES); + final Duration highResultDuration = + getAverageWorkTime(highAmountWork, "iterhigh", ITERATIVE_NUM_SAMPLES); + assertEquals( + "Work scale and CPU time scale do not match", + ((double) ITERATIVE_HIGH_ITERATIONS) / ((double) ITERATIVE_LOW_ITERATIONS), + ((double) highResultDuration.toMillis()) / ((double) lowResultDuration.toMillis()), + ITERATIONS_COMPARISONS_DELTA); + } + + /** + * Run some work {@code numSamples} times, and take the average CPU duration used for that work + * according to {@link KernelCpuThreadReader} + */ + private Duration getAverageWorkTime( + Runnable work, String tag, int numSamples) throws InterruptedException { + // Count down every time a thread finishes work, so that we can wait for work to complete + final CountDownLatch workFinishedLatch = new CountDownLatch(numSamples); + // Count down once when threads can terminate (after we get them from + // `KernelCpuThreadReader`) + final CountDownLatch threadFinishedLatch = new CountDownLatch(1); + + // Start `NUM_SAMPLE` threads to do the work + for (int i = 0; i < numSamples; i++) { + final String threadName = String.format("%s%d", tag, i); + // Check the thread name, as we rely on it later to identify threads + assertTrue("Max name length for linux threads is 15", threadName.length() <= 15); + doWork(work, threadName, workFinishedLatch, threadFinishedLatch); + } + + // Wait for threads to finish + workFinishedLatch.await(); + + // Get thread data from KernelCpuThreadReader + final KernelCpuThreadReader kernelCpuThreadReader = KernelCpuThreadReader.create(); + assertNotNull(kernelCpuThreadReader); + final ProcessCpuUsage currentProcessCpuUsage = + kernelCpuThreadReader.getCurrentProcessCpuUsage(); + + // Threads can terminate, as we've finished crawling them from /proc + threadFinishedLatch.countDown(); + + // Check that we've got times for every thread we spawned + final List<ThreadCpuUsage> threadCpuUsages = currentProcessCpuUsage.threadCpuUsages + .stream() + .filter((thread) -> thread.threadName.startsWith(tag)) + .collect(Collectors.toList()); + assertEquals( + "Incorrect number of threads returned by KernelCpuThreadReader", + numSamples, threadCpuUsages.size()); + + // Calculate the average time spent working + final OptionalDouble averageWorkTimeMillis = threadCpuUsages.stream() + .mapToDouble((t) -> Arrays.stream(t.usageTimesMillis).sum()) + .average(); + assertTrue(averageWorkTimeMillis.isPresent()); + return Duration.ofMillis((long) averageWorkTimeMillis.getAsDouble()); + } + + /** + * Work that lasts {@code duration} according to {@link SystemClock#currentThreadTimeMillis()} + */ + private Runnable timedWork(Duration duration) { + return () -> { + // Busy loop until `duration` has elapsed for the thread timer + final long startTimeMillis = SystemClock.currentThreadTimeMillis(); + final long durationMillis = duration.toMillis(); + while (true) { + final long elapsedMillis = SystemClock.currentThreadTimeMillis() - startTimeMillis; + if (elapsedMillis >= durationMillis) { + break; + } + } + }; + } + + /** + * Work that iterates {@code iterations} times + */ + private Runnable iterativeWork(long iterations) { + Consumer<Long> empty = (i) -> { + }; + return () -> { + long count = 0; + for (long i = 0; i < iterations; i++) { + // Alternate branching to reduce effect of branch prediction + if (i % 2 == 0) { + count++; + } + } + // Call empty function with value to avoid loop getting optimized away + empty.accept(count); + }; + } + + /** + * Perform some work in another thread + * + * @param work the work to perform + * @param threadName the name of the spawned thread + * @param workFinishedLatch latch to register that the work has been completed + * @param threadFinishedLatch latch to pause termination of the thread until the latch is + * decremented + */ + private void doWork( + Runnable work, + String threadName, + CountDownLatch workFinishedLatch, + CountDownLatch threadFinishedLatch) { + Runnable workWrapped = () -> { + // Do the work + work.run(); + // Notify that the work is finished + workFinishedLatch.countDown(); + // Wait until `threadFinishLatch` has been released in order to keep the thread alive so + // we can see it in `proc` filesystem + try { + threadFinishedLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }; + new Thread(workWrapped, threadName).start(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java index b242a34cc703..0c56b8a3e8aa 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java @@ -23,9 +23,10 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java index adafda04d516..1b13a9927beb 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static org.junit.Assert.assertEquals; @@ -20,11 +21,12 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseLongArray; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; import org.junit.After; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java index ad20d84bfc60..2ea80da9e6d0 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static org.junit.Assert.assertArrayEquals; @@ -22,11 +23,12 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import org.junit.After; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java index 1d3a98a89d95..0b6fed386117 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static org.junit.Assert.assertArrayEquals; @@ -24,11 +25,12 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import org.junit.After; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java index 9b4512b8b9bd..8f81ea237a15 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.os; import static org.junit.Assert.assertArrayEquals; @@ -23,11 +24,12 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; import org.junit.After; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java index 32317eed203e..60dac8520d14 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java @@ -1,11 +1,27 @@ +/* + * 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. + */ + package com.android.internal.os; -import android.support.test.filters.SmallTest; import android.util.LongSparseLongArray; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; -import org.junit.Assert; import org.mockito.Mockito; import java.io.BufferedReader; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java index 29227f9f6a1b..479e19e10ba0 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java @@ -21,10 +21,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.os.KernelSingleUidTimeReader.Injector; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java index 28570e8fa747..12f6c188f18f 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java @@ -20,8 +20,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java index 85dce020ddd4..532f337fe2e1 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java @@ -21,10 +21,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.when; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java index 67c4e6161da6..6d2980b8bed0 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java @@ -24,10 +24,11 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.util.SparseArray; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,7 +54,7 @@ import java.util.Arrays; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner * * or * diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java index 4e4bb350739d..78b6843e535c 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java @@ -13,9 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java index fe627642418f..cb8a62c0936c 100644 --- a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java @@ -16,7 +16,9 @@ package com.android.internal.os; -import android.test.suitebuilder.annotation.Suppress; +import androidx.test.filters.Suppress; + +import junit.framework.TestCase; import java.io.PrintWriter; import java.io.StringWriter; @@ -25,8 +27,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import junit.framework.TestCase; - // this test causes a IllegalAccessError: superclass not accessible @Suppress public class LoggingPrintStreamTest extends TestCase { diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java index 37b4e41a38d6..0516bb7f74d7 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java @@ -30,8 +30,9 @@ import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; @@ -55,7 +56,7 @@ import java.util.Arrays; * Install: adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class com.android.internal.os.LongSamplingCounterArrayTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ @SmallTest @RunWith(AndroidJUnit4.class) diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java index 853bf8a079a6..d2f5735ce3c1 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java @@ -28,8 +28,9 @@ import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.os.Parcel; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index b65c1e6210a7..2c597b14c557 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -23,8 +23,9 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Assert; diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index b68f6b1273b9..c18445e26173 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -21,6 +21,7 @@ import android.os.Looper; import android.util.SparseIntArray; import com.android.internal.location.gnssmetrics.GnssMetrics; + import java.util.ArrayList; import java.util.Queue; import java.util.concurrent.Future; diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java index 14d62e0b2f9c..c592ab6a16ae 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java @@ -17,13 +17,13 @@ package com.android.internal.os; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.os.BatteryStats; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java index 2853c965e871..5862368f44d2 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -17,8 +17,8 @@ package com.android.internal.os; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java index 489e164e0b12..e97caf8c0631 100644 --- a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java @@ -20,9 +20,10 @@ import static org.junit.Assert.assertEquals; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java index 2893066af0d8..9db3f8a6e187 100644 --- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java @@ -22,9 +22,10 @@ import static org.junit.Assert.fail; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java index c051a1cdf052..85eafc57acc3 100644 --- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java @@ -15,15 +15,17 @@ */ package com.android.internal.os; + import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import android.content.Context; import android.os.FileUtils; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; @@ -36,7 +38,6 @@ import java.io.BufferedWriter; import java.io.File; import java.nio.file.Files; - /** * Test class for {@link StoragedUidIoStatsReader}. * diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java index 2a24881f67fa..8e0c1fe99933 100644 --- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java @@ -17,8 +17,6 @@ package com.android.internal.policy; import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.test.suitebuilder.annotation.SmallTest; import android.view.ActionMode; import android.view.ActionMode.Callback; import android.view.KeyEvent; @@ -32,7 +30,8 @@ import android.view.Window; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; -import java.util.List; +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; /** * Tests {@link PhoneWindow}'s {@link ActionMode} related methods. diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java index d4b8566b0345..6c2d630ac917 100644 --- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java @@ -28,12 +28,13 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.ActionMode; import android.view.ContextThemeWrapper; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.frameworks.coretests.R; import org.junit.Before; diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java index b3897ceb6e79..d0267355c92e 100644 --- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java @@ -13,9 +13,11 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.util; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; + import junit.framework.TestCase; import java.util.ArrayList; diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java index a44b86074ee2..4716312c59a8 100644 --- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.util; import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS; diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java index 4845c4ef28f8..b2a2265d30ae 100644 --- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java +++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java @@ -19,11 +19,8 @@ package com.android.internal.util; import junit.framework.TestCase; import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintWriter; import java.io.Writer; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; /** diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java index f00c48c96b5d..867152e720dd 100644 --- a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.internal.util; import junit.framework.TestCase; diff --git a/core/tests/coretests/src/android/util/TokenBucketTest.java b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java index f7ac20c7287f..6c50bce86638 100644 --- a/core/tests/coretests/src/android/util/TokenBucketTest.java +++ b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java @@ -18,6 +18,7 @@ package com.android.internal.util; import android.os.SystemClock; import android.text.format.DateUtils; + import junit.framework.TestCase; public class TokenBucketTest extends TestCase { @@ -54,9 +55,9 @@ public class TokenBucketTest extends TestCase { drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 100), 10); - drain(new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50), 50); - drain(new TokenBucket((int)DateUtils.HOUR_IN_MILLIS, 10), 10); - drain(new TokenBucket((int)DateUtils.DAY_IN_MILLIS, 200), 200); + drain(new TokenBucket((int) DateUtils.MINUTE_IN_MILLIS, 50), 50); + drain(new TokenBucket((int) DateUtils.HOUR_IN_MILLIS, 10), 10); + drain(new TokenBucket((int) DateUtils.DAY_IN_MILLIS, 200), 200); } public void testReset() { @@ -163,16 +164,16 @@ public class TokenBucketTest extends TestCase { void assertDuration(long expected, long elapsed) { String msg = String.format( - "expected elapsed time at least %d ms, but was %d ms", expected, elapsed); + "expected elapsed time at least %d ms, but was %d ms", expected, elapsed); elapsed += 1; // one millisecond extra guard assertTrue(msg, elapsed >= expected); } - void assertThrow(Fn fn) { - try { - fn.call(); - fail("expected n exception to be thrown."); - } catch (Throwable t) {} + void assertThrow(Fn fn) { + try { + fn.call(); + fail("expected n exception to be thrown."); + } catch (Throwable t) { } } interface Fn { void call(); } diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java index 912b7ec02b15..b3723664dc1b 100644 --- a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java @@ -18,11 +18,12 @@ package com.android.internal.widget; import android.content.Context; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; import android.view.ActionMode; import android.view.View; import android.view.ViewGroup; +import androidx.test.filters.SmallTest; + /** * Tests for {@link ActionBarContainer}. */ diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java index d782c0c0308d..d10f173328be 100644 --- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java @@ -29,9 +29,6 @@ import static org.junit.Assert.assertThat; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.DisplayCutout; import android.view.View; import android.view.View.OnApplyWindowInsetsListener; @@ -40,6 +37,10 @@ import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.Toolbar; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java b/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java index e21143d4392f..0fada06b8de4 100644 --- a/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java @@ -32,10 +32,11 @@ import static java.util.Collections.emptyList; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.support.test.InstrumentationRegistry; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.test.InstrumentationRegistry; + import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java index 1806b226e7b3..2e0dbb4f574e 100644 --- a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java @@ -19,12 +19,13 @@ package com.android.internal.widget; import static org.junit.Assert.assertTrue; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.text.Layout; import android.view.View.MeasureSpec; import android.widget.TextView; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java index f73950a648e0..6167c4b80cee 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import android.os.UserHandle; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java index 41082b7c7593..6af7c886ce9b 100644 --- a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java @@ -21,12 +21,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.text.Layout; import android.view.LayoutInflater; import android.view.View.MeasureSpec; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + import com.android.frameworks.coretests.R; import org.junit.Before; diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 0dd9d0904746..28a8afe434e4 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -318,6 +318,27 @@ public class HdmiAudioSystemClientTest { mMaxVolume = maxVolume; mIsMute = isMute; } + + @Override + public void setSystemAudioModeOnForAudioOnlySource() { + } + + @Override + public int getPhysicalAddress() { + return 0x0000; + } + + @Override + public void powerOffRemoteDevice(int logicalAddress, int powerStatus) { + } + + @Override + public void powerOnRemoteDevice(int logicalAddress, int powerStatus) { + } + + @Override + public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) { + } } } diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk index f3d98a88d485..afbcd4675a78 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk @@ -23,7 +23,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2 LOCAL_JAVA_LIBRARIES := android-support-multidex -LOCAL_STATIC_JAVA_LIBRARIES := android-support-test +LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules LOCAL_SDK_VERSION := 9 diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml index 0ab29591be18..01285e77e3ff 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml @@ -7,7 +7,7 @@ <uses-sdk android:minSdkVersion="9" /> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/> <instrumentation - android:name="android.support.test.runner.AndroidJUnitRunner" + android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.framework.multidexlegacytestservices" /> <application diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java index 900f20387c49..f2c72f001646 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java @@ -19,22 +19,26 @@ package com.android.framework.multidexlegacytestservices.test2; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.RandomAccessFile; import java.util.concurrent.TimeoutException; -import junit.framework.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; /** * Run the tests with: <code>adb shell am instrument -w - * com.android.framework.multidexlegacytestservices.test2/android.support.test.runner.AndroidJUnitRunner + * com.android.framework.multidexlegacytestservices.test2/androidx.test.runner.AndroidJUnitRunner * </code> */ @RunWith(AndroidJUnit4.class) diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 6ce81481c1d7..035ee108c6da 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -39,9 +39,6 @@ prebuilt_etc { name: "privapp-permissions-platform.xml", sub_dir: "permissions", src: "privapp-permissions-platform.xml", - required: [ - "privapp_whitelist_com.android.settings.intelligence", - ], } prebuilt_etc { @@ -86,6 +83,7 @@ prebuilt_etc { prebuilt_etc { name: "privapp_whitelist_com.android.settings.intelligence", + product_specific: true, sub_dir: "permissions", src: "com.android.settings.intelligence.xml", filename_from_src: true, diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 4a2db0a6bb54..fb43e41010b4 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -223,12 +223,12 @@ code to link against. --> <library name="android.test.base" - file="/system/framework/android.test.base.impl.jar" /> + file="/system/framework/android.test.base.jar" /> <library name="android.test.mock" - file="/system/framework/android.test.mock.impl.jar" + file="/system/framework/android.test.mock.jar" dependency="android.test.base" /> <library name="android.test.runner" - file="/system/framework/android.test.runner.impl.jar" + file="/system/framework/android.test.runner.jar" dependency="android.test.base:android.test.mock" /> <!-- In BOOT_JARS historically, and now added to legacy applications. --> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 3342fd22caab..cbb780deac8a 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -17,12 +17,14 @@ package android.graphics; import android.annotation.ColorInt; +import android.annotation.ColorLong; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.Size; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontVariationAxis; import android.os.Build; @@ -972,6 +974,31 @@ public class Paint { } /** + * Set the paint's color with a {@link ColorLong}. Note that the color is + * a long with an encoded {@link ColorSpace} as well as alpha and r,g,b. + * These values are not premultiplied, meaning that alpha can be any value, + * regardless of the values of r,g,b. See the {@link Color} class for more + * details. + * + * @param color The new color (including alpha and {@link ColorSpace}) + * to set in the paint. + * @throws IllegalArgumentException if the color space encoded in the long + * is invalid or unknown. + * + * @hide pending API approval + */ + @TestApi + public void setColor(@ColorLong long color) { + ColorSpace cs = Color.colorSpace(color); + float r = Color.red(color); + float g = Color.green(color); + float b = Color.blue(color); + float a = Color.alpha(color); + + nSetColor(mNativePaint, cs, r, g, b, a); + } + + /** * Helper to getColor() that just returns the color's alpha value. This is * the same as calling getColor() >>> 24. It always returns a value between * 0 (completely transparent) and 255 (completely opaque). @@ -1370,12 +1397,45 @@ public class Paint { * The alpha of the shadow will be the paint's alpha if the shadow color is * opaque, or the alpha from the shadow color if not. */ - public void setShadowLayer(float radius, float dx, float dy, int shadowColor) { - mShadowLayerRadius = radius; - mShadowLayerDx = dx; - mShadowLayerDy = dy; - mShadowLayerColor = shadowColor; - nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor); + public void setShadowLayer(float radius, float dx, float dy, @ColorInt int shadowColor) { + mShadowLayerRadius = radius; + mShadowLayerDx = dx; + mShadowLayerDy = dy; + mShadowLayerColor = shadowColor; + // FIXME: Share a single native method with the ColorLong version. + nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor); + } + + /** + * This draws a shadow layer below the main layer, with the specified + * offset and color, and blur radius. If radius is 0, then the shadow + * layer is removed. + * <p> + * Can be used to create a blurred shadow underneath text. Support for use + * with other drawing operations is constrained to the software rendering + * pipeline. + * <p> + * The alpha of the shadow will be the paint's alpha if the shadow color is + * opaque, or the alpha from the shadow color if not. + * + * @throws IllegalArgumentException if the color space encoded in the long + * is invalid or unknown. + * + * @hide pending API approval + */ + @TestApi + public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) { + ColorSpace cs = Color.colorSpace(shadowColor); + float r = Color.red(shadowColor); + float g = Color.green(shadowColor); + float b = Color.blue(shadowColor); + float a = Color.alpha(shadowColor); + nSetShadowLayer(mNativePaint, radius, dx, dy, cs, r, g, b, a); + + mShadowLayerRadius = radius; + mShadowLayerDx = dx; + mShadowLayerDy = dy; + mShadowLayerColor = Color.toArgb(shadowColor); } /** @@ -2906,6 +2966,11 @@ public class Paint { int contextStart, int contextEnd, boolean isRtl, int offset); private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance); + private static native void nSetColor(long paintPtr, ColorSpace cs, + float r, float g, float b, float a); + private static native void nSetShadowLayer(long paintPtr, + float radius, float dx, float dy, ColorSpace cs, + float r, float g, float b, float a); // ---------------- @FastNative ------------------------ @@ -2961,7 +3026,7 @@ public class Paint { int mMinikinLocaleListId); @CriticalNative private static native void nSetShadowLayer(long paintPtr, - float radius, float dx, float dy, int color); + float radius, float dx, float dy, @ColorInt int color); @CriticalNative private static native boolean nHasShadowLayer(long paintPtr); @CriticalNative diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 3dc1f2cd56c5..18d74efdbacf 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -286,6 +286,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; config.density = 0; + uint32_t source_style_resid = 0; // Try to find a value for this attribute... we prioritize values // coming from, first XML attributes, then XML style, then default @@ -309,6 +310,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, cookie = entry->cookie; type_set_flags = style_flags; value = entry->value; + source_style_resid = entry->style; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, entry->style); @@ -325,8 +327,10 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, type_set_flags = def_style_flags; value = entry->value; if (kDebugStyles) { - ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); + ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, + entry->style); } + source_style_resid = entry->style; } } @@ -382,6 +386,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; + out_values[SYTLE_SOURCE_STYLE] = source_style_resid; if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { indices_idx++; diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 35ef98d8c704..c88004c70687 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -23,15 +23,17 @@ namespace android { // Offsets into the outValues array populated by the methods below. outValues is a uint32_t -// array, but each logical element takes up 6 uint32_t-sized physical elements. +// array, but each logical element takes up 7 uint32_t-sized physical elements. +// Keep these in sync with android.content.res.TypedArray java class enum { - STYLE_NUM_ENTRIES = 6, + STYLE_NUM_ENTRIES = 7, STYLE_TYPE = 0, STYLE_DATA = 1, STYLE_ASSET_COOKIE = 2, STYLE_RESOURCE_ID = 3, STYLE_CHANGING_CONFIGURATIONS = 4, - STYLE_DENSITY = 5 + STYLE_DENSITY = 5, + SYTLE_SOURCE_STYLE = 6 }; // These are all variations of the same method. They each perform the exact same operation, diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 6b9ebd3e8d12..1655e89a9b97 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1643,10 +1643,6 @@ struct ResTable_overlayable_policy_header // The overlay must reside of the product partition or must have existed on the product // partition before an upgrade to overlay these resources. POLICY_PRODUCT_PARTITION = 0x00000008, - - // The overlay must reside of the product services partition or must have existed on the product - // services partition before an upgrade to overlay these resources. - POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010, }; uint32_t policy_flags; diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 2e386a083185..b8d3c6bf92fb 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -312,7 +312,6 @@ TEST(LoadedArscTest, LoadOverlayable) { EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable")); EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable4); diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk Binary files differindex 863474794d00..047e6afde86b 100644 --- a/libs/androidfw/tests/data/overlayable/overlayable.apk +++ b/libs/androidfw/tests/data/overlayable/overlayable.apk diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml index dba7b08628f1..fcdbe94466c1 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -32,9 +32,9 @@ </overlayable> <overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable"> - <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of + <!-- Any overlay on the vendor or product partition can overlay the value of @string/overlayable3 --> - <policy type="product_services|vendor|product"> + <policy type="vendor|product"> <item type="string" name="overlayable3" /> </policy> </overlayable> diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index 9170d6d1dc50..68541b4b31f0 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -86,6 +86,26 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) { mCallbacks.gles.draw(mFunctor, mData, drawInfo); } +void WebViewFunctor::initVk(const VkFunctorInitParams& params) { + ATRACE_NAME("WebViewFunctor::initVk"); + if (!mHasContext) { + mHasContext = true; + } else { + return; + } + mCallbacks.vk.initialize(mFunctor, mData, params); +} + +void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) { + ATRACE_NAME("WebViewFunctor::drawVk"); + mCallbacks.vk.draw(mFunctor, mData, params); +} + +void WebViewFunctor::postDrawVk() { + ATRACE_NAME("WebViewFunctor::postDrawVk"); + mCallbacks.vk.postDraw(mFunctor, mData); +} + void WebViewFunctor::destroyContext() { if (mHasContext) { mHasContext = false; diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h index 1719ce7cca75..2846cb1f087b 100644 --- a/libs/hwui/WebViewFunctorManager.h +++ b/libs/hwui/WebViewFunctorManager.h @@ -42,6 +42,12 @@ public: void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); } + void initVk(const VkFunctorInitParams& params) { mReference.initVk(params); } + + void drawVk(const VkFunctorDrawParams& params) { mReference.drawVk(params); } + + void postDrawVk() { mReference.postDrawVk(); } + private: friend class WebViewFunctor; @@ -53,6 +59,9 @@ public: int id() const { return mFunctor; } void sync(const WebViewSyncData& syncData) const; void drawGl(const DrawGlInfo& drawInfo); + void initVk(const VkFunctorInitParams& params); + void drawVk(const VkFunctorDrawParams& params); + void postDrawVk(); void destroyContext(); sp<Handle> createHandle() { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 4338b1cc2a21..e6e6b0e62305 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -96,7 +96,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkASSERT(mRenderThread.getGrContext() != nullptr); sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget( - mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType, + mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType, mSurfaceColorSpace, &props)); SkiaPipeline::updateLighting(lightGeometry, lightInfo); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 479910697871..66929226a5e2 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -39,6 +39,7 @@ public: const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; + GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 2e7850d48e54..d7faaf707aa8 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -174,7 +174,8 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - SkBudgeted::kYes, info, 0, &props)); + SkBudgeted::kYes, info, 0, + this->getSurfaceOrigin(), &props)); if (node->getLayerSurface()) { // update the transform in window of the layer to reset its origin wrt light source // position diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index ff873133c6fb..94a699bfee31 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -48,7 +48,7 @@ public: bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; - SkColorType getSurfaceColorType() const { return mSurfaceColorType; } + SkColorType getSurfaceColorType() const override { return mSurfaceColorType; } sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; } void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 6eefed959913..d54275fe7e19 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -125,8 +125,6 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) { FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the - // interop is disabled/moved. functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>( functor, listener, asSkCanvas()); } else { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 53ffc4422fd7..934307636913 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -35,6 +35,7 @@ public: const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; + GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 156f74a611a7..2f8d381f15f5 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -17,6 +17,8 @@ #include "VkFunctorDrawable.h" #include <private/hwui/DrawVkInfo.h> +#include "renderthread/VulkanManager.h" +#include "renderthread/RenderThread.h" #include <GrBackendDrawableInfo.h> #include <SkImage.h> #include <utils/Color.h> @@ -31,34 +33,58 @@ namespace android { namespace uirenderer { namespace skiapipeline { -VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {} +VkFunctorDrawHandler::VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, + const SkMatrix& matrix, const SkIRect& clip, + const SkImageInfo& image_info) + : INHERITED() + , mFunctorHandle(functor_handle) + , mMatrix(matrix) + , mClip(clip) + , mImageInfo(image_info) {} VkFunctorDrawHandler::~VkFunctorDrawHandler() { - // TODO(cblume) Fill in the DrawVkInfo parameters. - (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr); + mFunctorHandle->postDrawVk(); } void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { ATRACE_CALL(); + if (!renderthread::RenderThread::isCurrent()) + LOG_ALWAYS_FATAL("VkFunctorDrawHandler::draw not called on render thread"); GrVkDrawableInfo vulkan_info; if (!info.getVkDrawableInfo(&vulkan_info)) { return; } + renderthread::VulkanManager& vk_manager = + renderthread::RenderThread::getInstance().vulkanManager(); + mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams()); - DrawVkInfo draw_vk_info; - // TODO(cblume) Fill in the rest of the parameters and test the actual call. - draw_vk_info.isLayer = true; + SkMatrix44 mat4(mMatrix); + VkFunctorDrawParams params{ + .width = mImageInfo.width(), + .height = mImageInfo.height(), + .is_layer = false, // TODO(boliu): Populate is_layer. + .color_space_ptr = mImageInfo.colorSpace(), + .clip_left = mClip.fLeft, + .clip_top = mClip.fTop, + .clip_right = mClip.fRight, + .clip_bottom = mClip.fBottom, + }; + mat4.asColMajorf(¶ms.transform[0]); + params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer; + params.color_attachment_index = vulkan_info.fColorAttachmentIndex; + params.compatible_render_pass = vulkan_info.fCompatibleRenderPass; + params.format = vulkan_info.fFormat; - (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info); + mFunctorHandle->drawVk(params); + + vulkan_info.fDrawBounds->offset.x = mClip.fLeft; + vulkan_info.fDrawBounds->offset.y = mClip.fTop; + vulkan_info.fDrawBounds->extent.width = mClip.fRight - mClip.fLeft; + vulkan_info.fDrawBounds->extent.height = mClip.fBottom - mClip.fTop; } VkFunctorDrawable::~VkFunctorDrawable() { - if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { - if (lp->listener) { - lp->listener->onGlFunctorReleased(lp->functor); - } - } } void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { @@ -67,16 +93,17 @@ void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { } std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler( - GrBackendApi backendApi, const SkMatrix& matrix) { + GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip, + const SkImageInfo& image_info) { if (backendApi != GrBackendApi::kVulkan) { return nullptr; } std::unique_ptr<VkFunctorDrawHandler> draw; if (mAnyFunctor.index() == 0) { - LOG_ALWAYS_FATAL("Not implemented"); - return nullptr; + return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip, + image_info); } else { - return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor); + LOG_ALWAYS_FATAL("Not implemented"); } } diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h index d6fefc1fca06..1a53c8fd55db 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h @@ -32,15 +32,18 @@ namespace skiapipeline { */ class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler { public: - explicit VkFunctorDrawHandler(Functor* functor); + VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, const SkMatrix& matrix, + const SkIRect& clip, const SkImageInfo& image_info); ~VkFunctorDrawHandler() override; void draw(const GrBackendDrawableInfo& info) override; private: typedef GpuDrawHandler INHERITED; - - Functor* mFunctor; + sp<WebViewFunctor::Handle> mFunctorHandle; + const SkMatrix mMatrix; + const SkIRect mClip; + const SkImageInfo mImageInfo; }; /** @@ -57,7 +60,8 @@ protected: // SkDrawable functions: void onDraw(SkCanvas* canvas) override; std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler( - GrBackendApi backendApi, const SkMatrix& matrix) override; + GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip, + const SkImageInfo& image_info) override; }; } // namespace skiapipeline diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h index fd824bd957fd..abc4dbf9fa1c 100644 --- a/libs/hwui/private/hwui/DrawVkInfo.h +++ b/libs/hwui/private/hwui/DrawVkInfo.h @@ -17,80 +17,61 @@ #ifndef ANDROID_HWUI_DRAW_VK_INFO_H #define ANDROID_HWUI_DRAW_VK_INFO_H +#include <SkColorSpace.h> #include <vulkan/vulkan.h> namespace android { namespace uirenderer { -/** - * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan - * functors. - */ -struct DrawVkInfo { - // Input: current width/height of destination surface - int width; - int height; - - // Input: is the render target an FBO - bool isLayer; - - // Input: current transform matrix, in OpenGL format - float transform[16]; - - // Input: WebView should do its main compositing draws into this. It cannot do anything that - // would require stopping the render pass. - VkCommandBuffer secondaryCommandBuffer; - - // Input: The main color attachment index where secondaryCommandBuffer will eventually be - // submitted. - uint32_t colorAttachmentIndex; - - // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer - // will be submitted into. - VkRenderPass compatibleRenderPass; - - // Input: Format of the destination surface. - VkFormat format; - - // Input: Color space - const SkColorSpace* colorSpaceInfo; - - // Input: current clip rect - int clipLeft; - int clipTop; - int clipRight; - int clipBottom; - - /** - * Values used as the "what" parameter of the functor. - */ - enum Mode { - // Called once at WebView start - kModeInit, - // Called when things need to be re-created - kModeReInit, - // Notifies the app that the composite functor will be called soon. This allows WebView to - // begin work early. - kModePreComposite, - // Do the actual composite work - kModeComposite, - // This allows WebView to begin using the previously submitted objects in future work. - kModePostComposite, - // Invoked every time the UI thread pushes over a frame to the render thread and the owning - // view has a dirty display list*. This is a signal to sync any data that needs to be - // shared between the UI thread and the render thread. During this time the UI thread is - // blocked. - kModeSync - }; - - /** - * Values used by Vulkan functors to tell the framework what to do next. - */ - enum Status { - // The functor is done - kStatusDone = 0x0, - }; -}; // struct DrawVkInfo +struct VkFunctorInitParams { + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; + VkQueue queue; + uint32_t graphics_queue_index; + uint32_t instance_version; + const char* const* enabled_instance_extension_names; + uint32_t enabled_instance_extension_names_length; + const char* const* enabled_device_extension_names; + uint32_t enabled_device_extension_names_length; + const VkPhysicalDeviceFeatures2* device_features_2; +}; + +struct VkFunctorDrawParams { + // Input: current width/height of destination surface. + int width; + int height; + + // Input: is the render target a FBO + bool is_layer; + + // Input: current transform matrix + float transform[16]; + + // Input WebView should do its main compositing draws into this. It cannot do + // anything that would require stopping the render pass. + VkCommandBuffer secondary_command_buffer; + + // Input: The main color attachment index where secondary_command_buffer will + // eventually be submitted. + uint32_t color_attachment_index; + + // Input: A render pass which will be compatible to the one which the + // secondary_command_buffer will be submitted into. + VkRenderPass compatible_render_pass; + + // Input: Format of the destination surface. + VkFormat format; + + // Input: Color space. + const SkColorSpace* color_space_ptr; + + // Input: current clip rect + int clip_left; + int clip_top; + int clip_right; + int clip_bottom; +}; } // namespace uirenderer } // namespace android diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h index da3d06a4d60c..96da947ace08 100644 --- a/libs/hwui/private/hwui/WebViewFunctor.h +++ b/libs/hwui/private/hwui/WebViewFunctor.h @@ -19,6 +19,7 @@ #include <cutils/compiler.h> #include <private/hwui/DrawGlInfo.h> +#include <private/hwui/DrawVkInfo.h> namespace android::uirenderer { @@ -52,18 +53,12 @@ struct WebViewFunctorCallbacks { // Called on RenderThread. initialize is guaranteed to happen before this call void (*draw)(int functor, void* data, const DrawGlInfo& params); } gles; - // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for - // what params are valid on what callbacks struct { // Called either the first time the functor is used or the first time it's used after // a call to onContextDestroyed. - // void (*initialize)(int functor, const InitParams& params); - // void (*frameStart)(int functor, /* todo: what params are actually needed for this to - // be useful? Is this useful? */) - // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite - // almost always means something else, and we aren't compositing */); - // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as - // CompositeParams - rename */); + void (*initialize)(int functor, void* data, const VkFunctorInitParams& params); + void (*draw)(int functor, void* data, const VkFunctorDrawParams& params); + void (*postDraw)(int functor, void*); } vk; }; }; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 42e17b273bee..d4dd62941440 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -84,6 +84,7 @@ public: virtual void onPrepareTree() = 0; virtual SkColorType getSurfaceColorType() const = 0; virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0; + virtual GrSurfaceOrigin getSurfaceOrigin() = 0; virtual ~IRenderPipeline() {} }; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index c06faddf7fa6..8bef35915c4d 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -171,6 +171,9 @@ void RenderThread::initThreadLocals() { mRenderState = new RenderState(*this); mVkManager = new VulkanManager(*this); mCacheManager = new CacheManager(mDisplayInfo); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + mVkManager->initialize(); + } } void RenderThread::requireGlContext() { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 5272227509c8..1ef83fb26c14 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -47,6 +47,10 @@ class Readback; class RenderState; class TestUtils; +namespace skiapipeline { +class VkFunctorDrawHandler; +} + namespace renderthread { class CanvasContext; @@ -124,6 +128,7 @@ private: friend class DummyVsyncSource; friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; + friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler; RenderThread(); virtual ~RenderThread(); diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index aa7a141f6da3..6c540f627a68 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -34,6 +34,23 @@ namespace android { namespace uirenderer { namespace renderthread { +static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) { + // All Vulkan structs that could be part of the features chain will start with the + // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader + // so we can get access to the pNext for the next struct. + struct CommonVulkanHeader { + VkStructureType sType; + void* pNext; + }; + + void* pNext = features.pNext; + while (pNext) { + void* current = pNext; + pNext = static_cast<CommonVulkanHeader*>(current)->pNext; + free(current); + } +} + #define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F) #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) @@ -66,6 +83,11 @@ void VulkanManager::destroy() { mDevice = VK_NULL_HANDLE; mPhysicalDevice = VK_NULL_HANDLE; mInstance = VK_NULL_HANDLE; + mInstanceVersion = 0u; + mInstanceExtensions.clear(); + mDeviceExtensions.clear(); + free_features_extensions_structs(mPhysicalDeviceFeatures2); + mPhysicalDeviceFeatures2 = {}; } bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) { @@ -81,7 +103,6 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe VK_MAKE_VERSION(1, 1, 0), // apiVersion }; - std::vector<const char*> instanceExtensions; { GET_PROC(EnumerateInstanceExtensionProperties); @@ -99,7 +120,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe bool hasKHRSurfaceExtension = false; bool hasKHRAndroidSurfaceExtension = false; for (uint32_t i = 0; i < extensionCount; ++i) { - instanceExtensions.push_back(extensions[i].extensionName); + mInstanceExtensions.push_back(extensions[i].extensionName); if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) { hasKHRSurfaceExtension = true; } @@ -120,8 +141,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe &app_info, // pApplicationInfo 0, // enabledLayerNameCount nullptr, // ppEnabledLayerNames - (uint32_t) instanceExtensions.size(), // enabledExtensionNameCount - instanceExtensions.data(), // ppEnabledExtensionNames + (uint32_t) mInstanceExtensions.size(), // enabledExtensionNameCount + mInstanceExtensions.data(), // ppEnabledExtensionNames }; GET_PROC(CreateInstance); @@ -201,7 +222,6 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe // presentation with any native window. So just use the first one. mPresentQueueIndex = 0; - std::vector<const char*> deviceExtensions; { uint32_t extensionCount = 0; err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, @@ -220,7 +240,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } bool hasKHRSwapchainExtension = false; for (uint32_t i = 0; i < extensionCount; ++i) { - deviceExtensions.push_back(extensions[i].extensionName); + mDeviceExtensions.push_back(extensions[i].extensionName); if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { hasKHRSwapchainExtension = true; } @@ -237,8 +257,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } return vkGetInstanceProcAddr(instance, proc_name); }; - grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(), - instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data()); + grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(), + mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data()); if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { this->destroy(); @@ -308,8 +328,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe queueInfo, // pQueueCreateInfos 0, // layerCount nullptr, // ppEnabledLayerNames - (uint32_t) deviceExtensions.size(), // extensionCount - deviceExtensions.data(), // ppEnabledExtensionNames + (uint32_t) mDeviceExtensions.size(), // extensionCount + mDeviceExtensions.data(), // ppEnabledExtensionNames nullptr, // ppEnabledFeatures }; @@ -351,36 +371,17 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe return true; } -static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) { - // All Vulkan structs that could be part of the features chain will start with the - // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader - // so we can get access to the pNext for the next struct. - struct CommonVulkanHeader { - VkStructureType sType; - void* pNext; - }; - - void* pNext = features.pNext; - while (pNext) { - void* current = pNext; - pNext = static_cast<CommonVulkanHeader*>(current)->pNext; - free(current); - } -} - void VulkanManager::initialize() { if (mDevice != VK_NULL_HANDLE) { return; } GET_PROC(EnumerateInstanceVersion); - uint32_t instanceVersion = 0; - LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); - LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); + LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&mInstanceVersion)); + LOG_ALWAYS_FATAL_IF(mInstanceVersion < VK_MAKE_VERSION(1, 1, 0)); GrVkExtensions extensions; - VkPhysicalDeviceFeatures2 features; - LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features)); + LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2)); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); @@ -397,9 +398,9 @@ void VulkanManager::initialize() { backendContext.fDevice = mDevice; backendContext.fQueue = mGraphicsQueue; backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; - backendContext.fInstanceVersion = instanceVersion; + backendContext.fInstanceVersion = mInstanceVersion; backendContext.fVkExtensions = &extensions; - backendContext.fDeviceFeatures2 = &features; + backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; backendContext.fGetProc = std::move(getProc); // create the command pool for the command buffers @@ -433,13 +434,29 @@ void VulkanManager::initialize() { LOG_ALWAYS_FATAL_IF(!grContext.get()); mRenderThread.setGrContext(grContext); - free_features_extensions_structs(features); - if (Properties::enablePartialUpdates && Properties::useBufferAge) { mSwapBehavior = SwapBehavior::BufferAge; } } +VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { + return VkFunctorInitParams{ + .instance = mInstance, + .physical_device = mPhysicalDevice, + .device = mDevice, + .queue = mGraphicsQueue, + .graphics_queue_index = mGraphicsQueueIndex, + .instance_version = mInstanceVersion, + .enabled_instance_extension_names = mInstanceExtensions.data(), + .enabled_instance_extension_names_length = + static_cast<uint32_t>(mInstanceExtensions.size()), + .enabled_device_extension_names = mDeviceExtensions.data(), + .enabled_device_extension_names_length = + static_cast<uint32_t>(mDeviceExtensions.size()), + .device_features_2 = &mPhysicalDeviceFeatures2, + }; +} + // Returns the next BackbufferInfo to use for the next draw. The function will make sure all // previous uses have finished before returning. VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) { diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 9eb942c9d6ee..105ee093a057 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -132,6 +132,9 @@ public: // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. status_t createReleaseFence(sp<Fence>& nativeFence); + // Returned pointers are owned by VulkanManager. + VkFunctorInitParams getVkFunctorInitParams() const; + private: friend class RenderThread; @@ -234,6 +237,12 @@ private: VkCommandBuffer mDummyCB = VK_NULL_HANDLE; + // Variables saved to populate VkFunctorInitParams. + uint32_t mInstanceVersion = 0u; + std::vector<const char*> mInstanceExtensions; + std::vector<const char*> mDeviceExtensions; + VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures2{}; + enum class SwapBehavior { Discard, BufferAge, diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h index 75b26c626d14..07472435d8a3 100644 --- a/libs/services/include/android/os/DropBoxManager.h +++ b/libs/services/include/android/os/DropBoxManager.h @@ -62,7 +62,7 @@ public: // file descriptor. Status addFile(const String16& tag, int fd, int flags); - class Entry : public virtual RefBase, public Parcelable { + class Entry : public Parcelable { public: Entry(); virtual ~Entry(); @@ -89,9 +89,6 @@ public: friend class DropBoxManager; }; - // Get the next entry from the drop box after the specified time. - Status getNextEntry(const String16& tag, long msec, Entry* entry); - private: enum { HAS_BYTE_ARRAY = 8 diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp index 8282518f75c6..681d5f780739 100644 --- a/libs/services/src/os/DropBoxManager.cpp +++ b/libs/services/src/os/DropBoxManager.cpp @@ -228,15 +228,4 @@ DropBoxManager::add(const Entry& entry) return service->add(entry); } -Status -DropBoxManager::getNextEntry(const String16& tag, long msec, Entry* entry) -{ - sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>( - defaultServiceManager()->getService(android::String16("dropbox"))); - if (service == NULL) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service"); - } - return service->getNextEntry(tag, msec, android::String16("android"), entry); -} - }} // namespace android::os diff --git a/media/Android.bp b/media/Android.bp index d5da6f266952..8ebc91a06f7a 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -1,4 +1,21 @@ java_library { + name: "media1", + + srcs: [ + ":media1-srcs", + ], + + sdk_version: "system_current", +} + +filegroup { + name: "media1-srcs", + srcs: [ + "java/android/media/session/MediaSessionProviderService.java", + ], +} + +java_library { // TODO: include media2.jar in the media apex and add it to the bootclasspath. name: "media2", diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 4f23cca1171f..f5a6f8667fa6 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -736,8 +736,9 @@ public final class AudioAttributes implements Parcelable { * @param preset one of {@link MediaRecorder.AudioSource#DEFAULT}, * {@link MediaRecorder.AudioSource#MIC}, {@link MediaRecorder.AudioSource#CAMCORDER}, * {@link MediaRecorder.AudioSource#VOICE_RECOGNITION}, - * {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION} or - * {@link MediaRecorder.AudioSource#UNPROCESSED} + * {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}, + * {@link MediaRecorder.AudioSource#UNPROCESSED} or + * {@link MediaRecorder.AudioSource#VOICE_PERFORMANCE} * @return the same Builder instance. */ @SystemApi @@ -749,6 +750,7 @@ public final class AudioAttributes implements Parcelable { case MediaRecorder.AudioSource.VOICE_RECOGNITION: case MediaRecorder.AudioSource.VOICE_COMMUNICATION: case MediaRecorder.AudioSource.UNPROCESSED: + case MediaRecorder.AudioSource.VOICE_PERFORMANCE: mSource = preset; break; default: @@ -760,7 +762,7 @@ public final class AudioAttributes implements Parcelable { /** * @hide * Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD, - * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK and VOICE_CALL. + * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL and ECHO_REFERENCE. * @param preset * @return the same Builder instance. */ @@ -771,7 +773,8 @@ public final class AudioAttributes implements Parcelable { || (preset == MediaRecorder.AudioSource.RADIO_TUNER) || (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK) || (preset == MediaRecorder.AudioSource.VOICE_UPLINK) - || (preset == MediaRecorder.AudioSource.VOICE_CALL)) { + || (preset == MediaRecorder.AudioSource.VOICE_CALL) + || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)) { mSource = preset; } else { setCapturePreset(preset); diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 793aa270e2fd..55160869ad0a 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -816,7 +817,7 @@ public final class AudioFormat implements Parcelable { * * @return The audio frame size in bytes corresponding to the encoding and the channel mask. */ - public int getFrameSizeInBytes() { + public @IntRange(from = 1) int getFrameSizeInBytes() { return mFrameSizeInBytes; } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 33f81f1db69c..92afe7ede8f2 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -61,7 +61,8 @@ import java.util.concurrent.Executor; * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to * the total recording buffer size. */ -public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRecordingMonitorClient +public class AudioRecord implements AudioRouting, MicrophoneDirection, + AudioRecordingMonitor, AudioRecordingMonitorClient { //--------------------------------------------------------- // Constants @@ -1657,7 +1658,6 @@ public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRe return activeMicrophones; } - //-------------------------------------------------------------------------- // Implementation of AudioRecordingMonitor interface //-------------------- @@ -1707,6 +1707,33 @@ public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRe return native_getPortId(); } + //-------------------------------------------------------------------------- + // MicrophoneDirection + //-------------------- + /** + * Specifies the logical microphone (for processing). + * + * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*) + * @return retval OK if the call is successful, an error code otherwise. + * @hide + */ + public int setMicrophoneDirection(int direction) { + return native_set_microphone_direction(direction); + } + + /** + * Specifies the zoom factor (i.e. the field dimension) for the selected microphone + * (for processing). The selected microphone is determined by the use-case for the stream. + * + * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle), + * though 0 (no zoom) to 1 (maximum zoom). + * @return retval OK if the call is successful, an error code otherwise. + * @hide + */ + public int setMicrophoneFieldDimension(float zoom) { + return native_set_microphone_field_dimension(zoom); + } + //--------------------------------------------------------- // Interface definitions //-------------------- @@ -1860,6 +1887,9 @@ public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRe private native int native_getPortId(); + private native int native_set_microphone_direction(int direction); + private native int native_set_microphone_field_dimension(float zoom); + //--------------------------------------------------------- // Utility methods //------------------ diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java index de76aeff82c4..52771e4199d8 100644 --- a/media/java/android/media/AudioRecordingConfiguration.java +++ b/media/java/android/media/AudioRecordingConfiguration.java @@ -172,7 +172,8 @@ public final class AudioRecordingConfiguration implements Parcelable { MediaRecorder.AudioSource.CAMCORDER, MediaRecorder.AudioSource.VOICE_RECOGNITION, MediaRecorder.AudioSource.VOICE_COMMUNICATION, - MediaRecorder.AudioSource.UNPROCESSED + MediaRecorder.AudioSource.UNPROCESSED, + MediaRecorder.AudioSource.VOICE_PERFORMANCE }) @Retention(RetentionPolicy.SOURCE) public @interface AudioSource {} diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 45cde0ffecc4..de73649d59dc 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -541,6 +541,8 @@ public class AudioSystem public static final int DEVICE_IN_BUS = DEVICE_BIT_IN | 0x100000; public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000; public static final int DEVICE_IN_USB_HEADSET = DEVICE_BIT_IN | 0x2000000; + public static final int DEVICE_IN_BLUETOOTH_BLE = DEVICE_BIT_IN | 0x4000000; + public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000; @UnsupportedAppUsage public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT; @@ -567,6 +569,8 @@ public class AudioSystem DEVICE_IN_BUS | DEVICE_IN_PROXY | DEVICE_IN_USB_HEADSET | + DEVICE_IN_BLUETOOTH_BLE | + DEVICE_IN_ECHO_REFERENCE | DEVICE_IN_DEFAULT); public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET; public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY | @@ -641,6 +645,8 @@ public class AudioSystem public static final String DEVICE_IN_BUS_NAME = "bus"; public static final String DEVICE_IN_PROXY_NAME = "proxy"; public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset"; + public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble"; + public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference"; @UnsupportedAppUsage public static String getOutputDeviceName(int device) @@ -757,6 +763,10 @@ public class AudioSystem return DEVICE_IN_PROXY_NAME; case DEVICE_IN_USB_HEADSET: return DEVICE_IN_USB_HEADSET_NAME; + case DEVICE_IN_BLUETOOTH_BLE: + return DEVICE_IN_BLUETOOTH_BLE_NAME; + case DEVICE_IN_ECHO_REFERENCE: + return DEVICE_IN_ECHO_REFERENCE_NAME; case DEVICE_IN_DEFAULT: default: return Integer.toString(device); diff --git a/media/java/android/media/IMediaSession2Service.aidl b/media/java/android/media/IMediaSession2Service.aidl new file mode 100644 index 000000000000..10ac1be0a36e --- /dev/null +++ b/media/java/android/media/IMediaSession2Service.aidl @@ -0,0 +1,32 @@ +/* + * 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.media; + +import android.os.Bundle; +import android.media.Controller2Link; + +/** + * Interface from MediaController2 to MediaSession2Service. + * <p> + * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, + * and holds calls from controller to make controller owner(s) frozen. + * @hide + */ +oneway interface IMediaSession2Service { + void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0; + // Next Id : 1 +} diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index bc9500ddb3b7..f756658b4fa8 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -1829,9 +1829,14 @@ final public class MediaCodec { mBufferLock = new Object(); + // save name used at creation + mNameAtCreation = nameIsType ? null : name; + native_setup(name, nameIsType, encoder); } + private String mNameAtCreation; + @Override protected void finalize() { native_finalize(); @@ -3317,12 +3322,36 @@ final public class MediaCodec { private native void native_setAudioPresentation(int presentationId, int programId); /** - * Get the component name. If the codec was created by createDecoderByType - * or createEncoderByType, what component is chosen is not known beforehand. + * Retrieve the codec name. + * + * If the codec was created by createDecoderByType or createEncoderByType, what component is + * chosen is not known beforehand. This method returns the name of the codec that was + * selected by the platform. + * + * <strong>Note:</strong> Implementations may provide multiple aliases (codec + * names) for the same underlying codec, any of which can be used to instantiate the same + * underlying codec in {@link MediaCodec#createByCodecName}. This method returns the + * name used to create the codec in this case. + * + * @throws IllegalStateException if in the Released state. + */ + @NonNull + public final String getName() { + // get canonical name to handle exception + String canonicalName = getCanonicalName(); + return mNameAtCreation != null ? mNameAtCreation : canonicalName; + } + + /** + * Retrieve the underlying codec name. + * + * This method is similar to {@link #getName}, except that it returns the underlying component + * name even if an alias was used to create this MediaCodec object by name, + * * @throws IllegalStateException if in the Released state. */ @NonNull - public native final String getName(); + public native final String getCanonicalName(); /** * Return Metrics data about the current codec instance. diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 10a1e3a72225..751d57bbdc81 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -32,8 +32,10 @@ import android.util.Size; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Vector; /** * Provides information about a given media codec available on the device. You can @@ -61,15 +63,25 @@ import java.util.Set; * */ public final class MediaCodecInfo { - private boolean mIsEncoder; + private static final String TAG = "MediaCodecInfo"; + + private static final int FLAG_IS_ENCODER = (1 << 0); + private static final int FLAG_IS_VENDOR = (1 << 1); + private static final int FLAG_IS_SOFTWARE_ONLY = (1 << 2); + private static final int FLAG_IS_HARDWARE_ACCELERATED = (1 << 3); + + private int mFlags; private String mName; + private String mCanonicalName; private Map<String, CodecCapabilities> mCaps; /* package private */ MediaCodecInfo( - String name, boolean isEncoder, CodecCapabilities[] caps) { + String name, String canonicalName, int flags, CodecCapabilities[] caps) { mName = name; - mIsEncoder = isEncoder; + mCanonicalName = canonicalName; + mFlags = flags; mCaps = new HashMap<String, CodecCapabilities>(); + for (CodecCapabilities c: caps) { mCaps.put(c.getMimeType(), c); } @@ -77,16 +89,69 @@ public final class MediaCodecInfo { /** * Retrieve the codec name. + * + * <strong>Note:</strong> Implementations may provide multiple aliases (codec + * names) for the same underlying codec, any of which can be used to instantiate the same + * underlying codec in {@link MediaCodec#createByCodecName}. + * + * Applications targeting SDK < {@link android.os.Build.VERSION_CODES#Q}, cannot determine if + * the multiple codec names listed in MediaCodecList are in-fact for the same codec. */ + @NonNull public final String getName() { return mName; } /** + * Retrieve the underlying codec name. + * + * Device implementations may provide multiple aliases (codec names) for the same underlying + * codec to maintain backward app compatibility. This method returns the name of the underlying + * codec name, which must not be another alias. For non-aliases this is always the name of the + * codec. + */ + @NonNull + public final String getCanonicalName() { + return mCanonicalName; + } + + /** + * Query if the codec is an alias for another underlying codec. + */ + public final boolean isAlias() { + return !mName.equals(mCanonicalName); + } + + /** * Query if the codec is an encoder. */ public final boolean isEncoder() { - return mIsEncoder; + return (mFlags & FLAG_IS_ENCODER) != 0; + } + + /** + * Query if the codec is provided by the Android platform (false) or the device manufacturer + * (true). + */ + public final boolean isVendor() { + return (mFlags & FLAG_IS_VENDOR) != 0; + } + + /** + * Query if the codec is software only. Software-only codecs are more secure as they run in + * a tighter security sandbox. On the other hand, software-only codecs do not provide any + * performance guarantees. + */ + public final boolean isSoftwareOnly() { + return (mFlags & FLAG_IS_SOFTWARE_ONLY) != 0; + } + + /** + * Query if the codec is hardware accelerated. This attribute is provided by the device + * manufacturer. Note that it cannot be tested for correctness. + */ + public final boolean isHardwareAccelerated() { + return (mFlags & FLAG_IS_HARDWARE_ACCELERATED) != 0; } /** @@ -163,7 +228,7 @@ public final class MediaCodecInfo { // such as B-frame support, arithmetic coding... public CodecProfileLevel[] profileLevels; // NOTE this array is modifiable by user - // from OMX_COLOR_FORMATTYPE + // from MediaCodecConstants /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */ public static final int COLOR_FormatMonochrome = 1; /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */ @@ -344,7 +409,7 @@ public final class MediaCodecInfo { /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference. - // In OMX this is called OMX_COLOR_FormatAndroidOpaque. + // Note: in OMX this is called OMX_COLOR_FormatAndroidOpaque. public static final int COLOR_FormatSurface = 0x7F000789; /** @@ -435,8 +500,7 @@ public final class MediaCodecInfo { public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 0x7fa30c00; /** - * Defined in the OpenMAX IL specs, color format values are drawn from - * OMX_COLOR_FORMATTYPE. + * The color format for the media. This is one of the color constants defined in this class. */ public int[] colorFormats; // NOTE this array is modifiable by user @@ -462,6 +526,26 @@ public final class MediaCodecInfo { public static final String FEATURE_TunneledPlayback = "tunneled-playback"; /** + * If true, the timestamp of each output buffer is derived from the timestamp of the input + * buffer that produced the output. If false, the timestamp of each output buffer is + * derived from the timestamp of the first input buffer. + */ + public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp"; + + /** + * <b>decoder only</b>If true, the codec supports partial (including multiple) access units + * per input buffer. + */ + public static final String FEATURE_FrameParsing = "frame-parsing"; + + /** + * If true, the codec supports multiple access units (for decoding, or to output for + * encoders). If false, the codec only supports single access units. Producing multiple + * access units for output is an optional feature. + */ + public static final String FEATURE_MultipleFrames = "multiple-frames"; + + /** * <b>video decoder only</b>: codec supports queuing partial frames. */ public static final String FEATURE_PartialFrame = "partial-frame"; @@ -497,10 +581,15 @@ public final class MediaCodecInfo { new Feature(FEATURE_SecurePlayback, (1 << 1), false), new Feature(FEATURE_TunneledPlayback, (1 << 2), false), new Feature(FEATURE_PartialFrame, (1 << 3), false), + new Feature(FEATURE_FrameParsing, (1 << 4), false), + new Feature(FEATURE_MultipleFrames, (1 << 5), false), + new Feature(FEATURE_DynamicTimestamp, (1 << 6), false), }; private static final Feature[] encoderFeatures = { new Feature(FEATURE_IntraRefresh, (1 << 0), false), + new Feature(FEATURE_MultipleFrames, (1 << 1), false), + new Feature(FEATURE_DynamicTimestamp, (1 << 2), false), }; /** @hide */ @@ -869,7 +958,7 @@ public final class MediaCodecInfo { CodecCapabilities ret = new CodecCapabilities( new CodecProfileLevel[] { pl }, new int[0], true /* encoder */, - 0 /* flags */, defaultFormat, new MediaFormat() /* info */); + defaultFormat, new MediaFormat() /* info */); if (ret.mError != 0) { return null; } @@ -878,10 +967,10 @@ public final class MediaCodecInfo { /* package private */ CodecCapabilities( CodecProfileLevel[] profLevs, int[] colFmts, - boolean encoder, int flags, + boolean encoder, Map<String, Object>defaultFormatMap, Map<String, Object>capabilitiesMap) { - this(profLevs, colFmts, encoder, flags, + this(profLevs, colFmts, encoder, new MediaFormat(defaultFormatMap), new MediaFormat(capabilitiesMap)); } @@ -889,11 +978,11 @@ public final class MediaCodecInfo { private MediaFormat mCapabilitiesInfo; /* package private */ CodecCapabilities( - CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder, int flags, + CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder, MediaFormat defaultFormat, MediaFormat info) { final Map<String, Object> map = info.getMap(); colorFormats = colFmts; - mFlagsVerified = flags; + mFlagsVerified = 0; // TODO: remove as it is unused mDefaultFormat = defaultFormat; mCapabilitiesInfo = info; mMime = mDefaultFormat.getString(MediaFormat.KEY_MIME); @@ -1243,6 +1332,7 @@ public final class MediaCodecInfo { private Range<Rational> mBlockAspectRatioRange; private Range<Long> mBlocksPerSecondRange; private Map<Size, Range<Long>> mMeasuredFrameRates; + private Vector<PerformancePoint> mPerformancePoints; private Range<Integer> mFrameRateRange; private int mBlockWidth; @@ -1524,6 +1614,158 @@ public final class MediaCodecInfo { } /** + * Video performance points are a set of standard performance points defined by pixel rate. + */ + public static final class PerformancePoint { + /** + * Frame width in pixels. + */ + public final int width; + + /** + * Frame height in pixels. + */ + public final int height; + + /** + * Frame rate in frames per second. + */ + public final int frameRate; + + /* package private */ + PerformancePoint(int width_, int height_, int frameRate_) { + width = width_; + height = height_; + frameRate = frameRate_; + } + + /** + * Checks whether the performance point covers a media format. + * + * @param format Stream format considered + * + * @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; + + 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; + } + + @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 false; + } + + /** 480p 24fps */ + public static final PerformancePoint SD_24 = new PerformancePoint(720, 480, 24); + /** 576p 25fps */ + public static final PerformancePoint SD_25 = new PerformancePoint(720, 576, 25); + /** 480p 30fps */ + public static final PerformancePoint SD_30 = new PerformancePoint(720, 480, 30); + /** 480p 48fps */ + public static final PerformancePoint SD_48 = new PerformancePoint(720, 480, 48); + /** 576p 50fps */ + public static final PerformancePoint SD_50 = new PerformancePoint(720, 576, 50); + /** 480p 60fps */ + public static final PerformancePoint SD_60 = new PerformancePoint(720, 480, 60); + + /** 720p 24fps */ + public static final PerformancePoint HD_24 = new PerformancePoint(1280, 720, 24); + /** 720p 25fps */ + public static final PerformancePoint HD_25 = new PerformancePoint(1280, 720, 25); + /** 720p 30fps */ + public static final PerformancePoint HD_30 = new PerformancePoint(1280, 720, 30); + /** 720p 50fps */ + public static final PerformancePoint HD_50 = new PerformancePoint(1280, 720, 50); + /** 720p 60fps */ + public static final PerformancePoint HD_60 = new PerformancePoint(1280, 720, 60); + /** 720p 100fps */ + public static final PerformancePoint HD_100 = new PerformancePoint(1280, 720, 100); + /** 720p 120fps */ + public static final PerformancePoint HD_120 = new PerformancePoint(1280, 720, 120); + /** 720p 200fps */ + public static final PerformancePoint HD_200 = new PerformancePoint(1280, 720, 200); + /** 720p 240fps */ + public static final PerformancePoint HD_240 = new PerformancePoint(1280, 720, 240); + + /** 1080p 24fps */ + public static final PerformancePoint FHD_24 = new PerformancePoint(1920, 1080, 24); + /** 1080p 25fps */ + public static final PerformancePoint FHD_25 = new PerformancePoint(1920, 1080, 25); + /** 1080p 30fps */ + public static final PerformancePoint FHD_30 = new PerformancePoint(1920, 1080, 30); + /** 1080p 50fps */ + public static final PerformancePoint FHD_50 = new PerformancePoint(1920, 1080, 50); + /** 1080p 60fps */ + public static final PerformancePoint FHD_60 = new PerformancePoint(1920, 1080, 60); + /** 1080p 100fps */ + public static final PerformancePoint FHD_100 = new PerformancePoint(1920, 1080, 100); + /** 1080p 120fps */ + public static final PerformancePoint FHD_120 = new PerformancePoint(1920, 1080, 120); + /** 1080p 200fps */ + public static final PerformancePoint FHD_200 = new PerformancePoint(1920, 1080, 200); + /** 1080p 240fps */ + public static final PerformancePoint FHD_240 = new PerformancePoint(1920, 1080, 240); + + /** 2160p 24fps */ + public static final PerformancePoint UHD_24 = new PerformancePoint(3840, 2160, 24); + /** 2160p 25fps */ + public static final PerformancePoint UHD_25 = new PerformancePoint(3840, 2160, 25); + /** 2160p 30fps */ + public static final PerformancePoint UHD_30 = new PerformancePoint(3840, 2160, 30); + /** 2160p 50fps */ + public static final PerformancePoint UHD_50 = new PerformancePoint(3840, 2160, 50); + /** 2160p 60fps */ + public static final PerformancePoint UHD_60 = new PerformancePoint(3840, 2160, 60); + /** 2160p 100fps */ + public static final PerformancePoint UHD_100 = new PerformancePoint(3840, 2160, 100); + /** 2160p 120fps */ + public static final PerformancePoint UHD_120 = new PerformancePoint(3840, 2160, 120); + /** 2160p 200fps */ + public static final PerformancePoint UHD_200 = new PerformancePoint(3840, 2160, 200); + /** 2160p 240fps */ + public static final PerformancePoint UHD_240 = new PerformancePoint(3840, 2160, 240); + } + + /** + * Returns the supported performance points. May return {@code null} if the codec did not + * publish any performance point information (e.g. the vendor codecs have not been updated + * to the latest android release). May return an empty list if the codec published that + * if does not guarantee any performance points. + * <p> + * This is a performance guarantee provided by the device manufacturer for hardware codecs + * based on hardware capabilities of the device. + * <p> + * The returned list is sorted first by decreasing number of pixels, then by decreasing + * width, and finally by decreasing frame rate. + * Performance points assume a single active codec. For use cases where multiple + * codecs are active, should use that highest pixel count, and add the frame rates of + * each individual codec. + */ + @Nullable + public List<PerformancePoint> getSupportedPerformancePoints() { + if (mPerformancePoints == null) { + return null; + } + return new ArrayList<PerformancePoint>(mPerformancePoints); + } + + /** * Returns whether a given video size ({@code width} and * {@code height}) and {@code frameRate} combination is supported. */ @@ -1659,6 +1901,50 @@ public final class MediaCodecInfo { mSmallerDimensionUpperLimit = SIZE_RANGE.getUpper(); } + private @Nullable Vector<PerformancePoint> getPerformancePoints(Map<String, Object> map) { + Vector<PerformancePoint> ret = new Vector<>(); + final String prefix = "performance-point-"; + Set<String> keys = map.keySet(); + for (String key : keys) { + // looking for: performance-point-WIDTHxHEIGHT-range + if (!key.startsWith(prefix)) { + continue; + } + String subKey = key.substring(prefix.length()); + if (subKey.equals("none") && ret.size() == 0) { + // This means that component knowingly did not publish performance points. + // This is different from when the component forgot to publish performance + // points. + return ret; + } + String[] temp = key.split("-"); + if (temp.length != 4) { + continue; + } + String sizeStr = temp[2]; + Size size = Utils.parseSize(sizeStr, null); + if (size == null || size.getWidth() * size.getHeight() <= 0) { + continue; + } + Range<Long> range = Utils.parseLongRange(map.get(key), null); + if (range == null || range.getLower() < 0 || range.getUpper() < 0) { + continue; + } + ret.add(new PerformancePoint( + size.getWidth(), size.getHeight(), range.getLower().intValue())); + } + // check if the component specified no performance point indication + if (ret.size() == 0) { + return null; + } + + // 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))); + return ret; + } + private Map<Size, Range<Long>> getMeasuredFrameRates(Map<String, Object> map) { Map<Size, Range<Long>> ret = new HashMap<Size, Range<Long>>(); final String prefix = "measured-frame-rate-"; @@ -1770,6 +2056,7 @@ public final class MediaCodecInfo { blockRates = Utils.parseLongRange(map.get("blocks-per-second-range"), null); mMeasuredFrameRates = getMeasuredFrameRates(map); + mPerformancePoints = getPerformancePoints(map); Pair<Range<Integer>, Range<Integer>> sizeRanges = parseWidthHeightRanges(map.get("size-range")); if (sizeRanges != null) { @@ -2879,7 +3166,9 @@ public final class MediaCodecInfo { * {@link MediaCodecInfo.CodecCapabilities#profileLevels} field. */ public static final class CodecProfileLevel { - // from OMX_VIDEO_AVCPROFILETYPE + // These constants were originally in-line with OMX values, but this + // correspondence is no longer maintained. + public static final int AVCProfileBaseline = 0x01; public static final int AVCProfileMain = 0x02; public static final int AVCProfileExtended = 0x04; @@ -2890,7 +3179,6 @@ public final class MediaCodecInfo { public static final int AVCProfileConstrainedBaseline = 0x10000; public static final int AVCProfileConstrainedHigh = 0x80000; - // from OMX_VIDEO_AVCLEVELTYPE public static final int AVCLevel1 = 0x01; public static final int AVCLevel1b = 0x02; public static final int AVCLevel11 = 0x04; @@ -2908,8 +3196,10 @@ public final class MediaCodecInfo { public static final int AVCLevel5 = 0x4000; public static final int AVCLevel51 = 0x8000; public static final int AVCLevel52 = 0x10000; + public static final int AVCLevel6 = 0x20000; + public static final int AVCLevel61 = 0x40000; + public static final int AVCLevel62 = 0x80000; - // from OMX_VIDEO_H263PROFILETYPE public static final int H263ProfileBaseline = 0x01; public static final int H263ProfileH320Coding = 0x02; public static final int H263ProfileBackwardCompatible = 0x04; @@ -2920,7 +3210,6 @@ public final class MediaCodecInfo { public static final int H263ProfileInterlace = 0x80; public static final int H263ProfileHighLatency = 0x100; - // from OMX_VIDEO_H263LEVELTYPE public static final int H263Level10 = 0x01; public static final int H263Level20 = 0x02; public static final int H263Level30 = 0x04; @@ -2930,7 +3219,6 @@ public final class MediaCodecInfo { public static final int H263Level60 = 0x40; public static final int H263Level70 = 0x80; - // from OMX_VIDEO_MPEG4PROFILETYPE public static final int MPEG4ProfileSimple = 0x01; public static final int MPEG4ProfileSimpleScalable = 0x02; public static final int MPEG4ProfileCore = 0x04; @@ -2948,7 +3236,6 @@ public final class MediaCodecInfo { public static final int MPEG4ProfileAdvancedScalable = 0x4000; public static final int MPEG4ProfileAdvancedSimple = 0x8000; - // from OMX_VIDEO_MPEG4LEVELTYPE public static final int MPEG4Level0 = 0x01; public static final int MPEG4Level0b = 0x02; public static final int MPEG4Level1 = 0x04; @@ -2960,7 +3247,6 @@ public final class MediaCodecInfo { public static final int MPEG4Level5 = 0x80; public static final int MPEG4Level6 = 0x100; - // from OMX_VIDEO_MPEG2PROFILETYPE public static final int MPEG2ProfileSimple = 0x00; public static final int MPEG2ProfileMain = 0x01; public static final int MPEG2Profile422 = 0x02; @@ -2968,14 +3254,12 @@ public final class MediaCodecInfo { public static final int MPEG2ProfileSpatial = 0x04; public static final int MPEG2ProfileHigh = 0x05; - // from OMX_VIDEO_MPEG2LEVELTYPE public static final int MPEG2LevelLL = 0x00; public static final int MPEG2LevelML = 0x01; public static final int MPEG2LevelH14 = 0x02; public static final int MPEG2LevelHL = 0x03; public static final int MPEG2LevelHP = 0x04; - // from OMX_AUDIO_AACPROFILETYPE public static final int AACObjectMain = 1; public static final int AACObjectLC = 2; public static final int AACObjectSSR = 3; @@ -2990,16 +3274,13 @@ public final class MediaCodecInfo { /** xHE-AAC (includes USAC) */ public static final int AACObjectXHE = 42; - // from OMX_VIDEO_VP8LEVELTYPE public static final int VP8Level_Version0 = 0x01; public static final int VP8Level_Version1 = 0x02; public static final int VP8Level_Version2 = 0x04; public static final int VP8Level_Version3 = 0x08; - // from OMX_VIDEO_VP8PROFILETYPE public static final int VP8ProfileMain = 0x01; - // from OMX_VIDEO_VP9PROFILETYPE public static final int VP9Profile0 = 0x01; public static final int VP9Profile1 = 0x02; public static final int VP9Profile2 = 0x04; @@ -3010,7 +3291,6 @@ public final class MediaCodecInfo { public static final int VP9Profile2HDR10Plus = 0x4000; public static final int VP9Profile3HDR10Plus = 0x8000; - // from OMX_VIDEO_VP9LEVELTYPE public static final int VP9Level1 = 0x1; public static final int VP9Level11 = 0x2; public static final int VP9Level2 = 0x4; @@ -3026,14 +3306,12 @@ public final class MediaCodecInfo { public static final int VP9Level61 = 0x1000; public static final int VP9Level62 = 0x2000; - // from OMX_VIDEO_HEVCPROFILETYPE public static final int HEVCProfileMain = 0x01; public static final int HEVCProfileMain10 = 0x02; public static final int HEVCProfileMainStill = 0x04; public static final int HEVCProfileMain10HDR10 = 0x1000; public static final int HEVCProfileMain10HDR10Plus = 0x2000; - // from OMX_VIDEO_HEVCLEVELTYPE public static final int HEVCMainTierLevel1 = 0x1; public static final int HEVCHighTierLevel1 = 0x2; public static final int HEVCMainTierLevel2 = 0x4; @@ -3067,7 +3345,6 @@ public final class MediaCodecInfo { HEVCHighTierLevel51 | HEVCHighTierLevel52 | HEVCHighTierLevel6 | HEVCHighTierLevel61 | HEVCHighTierLevel62; - // from OMX_VIDEO_DOLBYVISIONPROFILETYPE public static final int DolbyVisionProfileDvavPer = 0x1; public static final int DolbyVisionProfileDvavPen = 0x2; public static final int DolbyVisionProfileDvheDer = 0x4; @@ -3079,7 +3356,6 @@ public final class MediaCodecInfo { public static final int DolbyVisionProfileDvheSt = 0x100; public static final int DolbyVisionProfileDvavSe = 0x200; - // from OMX_VIDEO_DOLBYVISIONLEVELTYPE public static final int DolbyVisionLevelHd24 = 0x1; public static final int DolbyVisionLevelHd30 = 0x2; public static final int DolbyVisionLevelFhd24 = 0x4; @@ -3090,17 +3366,44 @@ public final class MediaCodecInfo { public static final int DolbyVisionLevelUhd48 = 0x80; public static final int DolbyVisionLevelUhd60 = 0x100; + public static final int AV1Profile0 = 0x1; + public static final int AV1Profile1 = 0x2; + public static final int AV1Profile2 = 0x4; + + public static final int AV1Level2 = 0x1; + public static final int AV1Level21 = 0x2; + public static final int AV1Level22 = 0x4; + public static final int AV1Level23 = 0x8; + public static final int AV1Level3 = 0x10; + public static final int AV1Level31 = 0x20; + public static final int AV1Level32 = 0x40; + public static final int AV1Level33 = 0x80; + public static final int AV1Level4 = 0x100; + public static final int AV1Level41 = 0x200; + public static final int AV1Level42 = 0x400; + public static final int AV1Level43 = 0x800; + public static final int AV1Level5 = 0x1000; + public static final int AV1Level51 = 0x2000; + public static final int AV1Level52 = 0x4000; + public static final int AV1Level53 = 0x8000; + public static final int AV1Level6 = 0x10000; + public static final int AV1Level61 = 0x20000; + public static final int AV1Level62 = 0x40000; + public static final int AV1Level63 = 0x80000; + public static final int AV1Level7 = 0x100000; + public static final int AV1Level71 = 0x200000; + public static final int AV1Level72 = 0x400000; + public static final int AV1Level73 = 0x800000; + /** - * Defined in the OpenMAX IL specs, depending on the type of media - * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE, - * OMX_VIDEO_MPEG4PROFILETYPE, OMX_VIDEO_VP8PROFILETYPE or OMX_VIDEO_VP9PROFILETYPE. + * The profile of the media content. Depending on the type of media this can be + * one of the profile values defined in this class. */ public int profile; /** - * Defined in the OpenMAX IL specs, depending on the type of media - * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE - * OMX_VIDEO_MPEG4LEVELTYPE, OMX_VIDEO_VP8LEVELTYPE or OMX_VIDEO_VP9LEVELTYPE. + * The level of the media content. Depending on the type of media this can be + * one of the level values defined in this class. * * Note that VP9 decoder on platforms before {@link android.os.Build.VERSION_CODES#N} may * not advertise a profile level support. For those VP9 decoders, please use @@ -3157,7 +3460,7 @@ public final class MediaCodecInfo { } return new MediaCodecInfo( - mName, mIsEncoder, + mName, mCanonicalName, mFlags, caps.toArray(new CodecCapabilities[caps.size()])); } } diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java index 2e4786579d97..a46095484fe7 100644 --- a/media/java/android/media/MediaCodecList.java +++ b/media/java/android/media/MediaCodecList.java @@ -111,12 +111,14 @@ final public class MediaCodecList { caps[typeIx++] = getCodecCapabilities(index, type); } return new MediaCodecInfo( - getCodecName(index), isEncoder(index), caps); + getCodecName(index), getCanonicalName(index), getAttributes(index), caps); } /* package private */ static native final String getCodecName(int index); - /* package private */ static native final boolean isEncoder(int index); + /* package private */ static native final String getCanonicalName(int index); + + /* package private */ static native final int getAttributes(int index); /* package private */ static native final String[] getSupportedTypes(int index); diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java index 774ea185f57c..165ea412333e 100644 --- a/media/java/android/media/MediaController2.java +++ b/media/java/android/media/MediaController2.java @@ -26,12 +26,16 @@ import static android.media.Session2Token.TYPE_SESSION; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Process; +import android.os.RemoteException; import android.os.ResultReceiver; import android.util.ArrayMap; import android.util.ArraySet; @@ -63,6 +67,7 @@ public class MediaController2 implements AutoCloseable { private final Executor mCallbackExecutor; private final Controller2Link mControllerStub; private final Handler mResultHandler; + private final SessionServiceConnection mServiceConnection; private final Object mLock = new Object(); //@GuardedBy("mLock") @@ -118,16 +123,25 @@ public class MediaController2 implements AutoCloseable { mPendingCommands = new ArrayMap<>(); mRequestedCommandSeqNumbers = new ArraySet<>(); + boolean connectRequested; if (token.getType() == TYPE_SESSION) { - connectToSession(); + mServiceConnection = null; + connectRequested = requestConnectToSession(); } else { - // TODO: Handle connect to session service. + mServiceConnection = new SessionServiceConnection(); + connectRequested = requestConnectToService(); + } + if (!connectRequested) { + close(); } } @Override public void close() { synchronized (mLock) { + if (mServiceConnection != null) { + mContext.unbindService(mServiceConnection); + } if (mSessionBinder != null) { try { mSessionBinder.disconnect(mControllerStub, getNextSeqNumber()); @@ -299,18 +313,55 @@ public class MediaController2 implements AutoCloseable { } } - private void connectToSession() { - Session2Link sessionBinder = mSessionToken.getSessionLink(); + private Bundle createConnectionRequest() { Bundle connectionRequest = new Bundle(); connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName()); connectionRequest.putInt(KEY_PID, Process.myPid()); + return connectionRequest; + } + private boolean requestConnectToSession() { + Session2Link sessionBinder = mSessionToken.getSessionLink(); + Bundle connectionRequest = createConnectionRequest(); try { sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest); } catch (RuntimeException e) { - Log.w(TAG, "Failed to call connection request. Framework will retry" - + " automatically"); + Log.w(TAG, "Failed to call connection request", e); + return false; + } + return true; + } + + private boolean requestConnectToService() { + // Service. Needs to get fresh binder whenever connection is needed. + final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE); + intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName()); + + // Use bindService() instead of startForegroundService() to start session service for three + // reasons. + // 1. Prevent session service owner's stopSelf() from destroying service. + // With the startForegroundService(), service's call of stopSelf() will trigger immediate + // onDestroy() calls on the main thread even when onConnect() is running in another + // thread. + // 2. Minimize APIs for developers to take care about. + // With bindService(), developers only need to take care about Service.onBind() + // but Service.onStartCommand() should be also taken care about with the + // startForegroundService(). + // 3. Future support for UI-less playback + // If a service wants to keep running, it should be either foreground service or + // bound service. But there had been request for the feature for system apps + // and using bindService() will be better fit with it. + synchronized (mLock) { + boolean result = mContext.bindService( + intent, mServiceConnection, Context.BIND_AUTO_CREATE); + if (!result) { + Log.w(TAG, "bind to " + mSessionToken + " failed"); + return false; + } else if (DEBUG) { + Log.d(TAG, "bind to " + mSessionToken + " succeeded"); + } } + return true; } /** @@ -367,4 +418,59 @@ public class MediaController2 implements AutoCloseable { public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token, @NonNull Session2Command command, @NonNull Session2Command.Result result) {} } + + // This will be called on the main thread. + private class SessionServiceConnection implements ServiceConnection { + SessionServiceConnection() { + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + // Note that it's always main-thread. + boolean connectRequested = false; + try { + if (DEBUG) { + Log.d(TAG, "onServiceConnected " + name + " " + this); + } + // Sanity check + if (!mSessionToken.getPackageName().equals(name.getPackageName())) { + Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName() + + " but is connected to " + name); + return; + } + IMediaSession2Service iService = IMediaSession2Service.Stub.asInterface(service); + if (iService == null) { + Log.wtf(TAG, "Service interface is missing."); + return; + } + Bundle connectionRequest = createConnectionRequest(); + iService.connect(mControllerStub, getNextSeqNumber(), connectionRequest); + connectRequested = true; + } catch (RemoteException e) { + Log.w(TAG, "Service " + name + " has died prematurely", e); + } finally { + if (!connectRequested) { + close(); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // Temporal lose of the binding because of the service crash. System will automatically + // rebind, so just no-op. + if (DEBUG) { + Log.w(TAG, "Session service " + name + " is disconnected."); + } + close(); + } + + @Override + public void onBindingDied(ComponentName name) { + // Permanent lose of the binding because of the service package update or removed. + // This SessionServiceRecord will be removed accordingly, but forget session binder here + // for sure. + close(); + } + } } diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 594a22457cc3..c82b5f6f12a1 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -124,6 +124,7 @@ import java.util.stream.Collectors; public final class MediaFormat { public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; + public static final String MIMETYPE_VIDEO_AV1 = "video/av01"; public static final String MIMETYPE_VIDEO_AVC = "video/avc"; public static final String MIMETYPE_VIDEO_HEVC = "video/hevc"; public static final String MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index df96994b0c36..aa79c417922c 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -60,6 +60,7 @@ import java.net.URL; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -67,6 +68,7 @@ import java.util.List; import java.util.Map; import java.util.Queue; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -399,8 +401,7 @@ public class MediaPlayer2 implements AutoCloseable clearSourceInfos(); // Modular DRM clean up - mOnDrmConfigHelper = null; - synchronized (mDrmEventCbLock) { + synchronized (mDrmEventCallbackLock) { mDrmEventCallbackRecords.clear(); } @@ -775,7 +776,7 @@ public class MediaPlayer2 implements AutoCloseable } boolean hasError = false; for (DataSourceDesc dsd : dsds) { - if (dsd != null) { + if (dsd == null) { hasError = true; continue; } @@ -2889,7 +2890,7 @@ public class MediaPlayer2 implements AutoCloseable } private void sendDrmEvent(final DrmEventNotifier notifier) { - synchronized (mDrmEventCbLock) { + synchronized (mDrmEventCallbackLock) { try { for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { cb.first.execute(() -> notifier.notify(cb.second)); @@ -2901,12 +2902,28 @@ public class MediaPlayer2 implements AutoCloseable } } + private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier) + throws InterruptedException, ExecutionException { + synchronized (mDrmEventCallbackLock) { + mDrmEventCallbackRecords.get(0); + for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { + CompletableFuture<T> ret = new CompletableFuture<>(); + cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second))); + return ret.get(); + } + } + return null; + } + private interface EventNotifier { void notify(EventCallback callback); } - private interface DrmEventNotifier { - void notify(DrmEventCallback callback); + private interface DrmEventNotifier<T> { + default void notify(DrmEventCallback callback) { } + default T notifyWait(DrmEventCallback callback) { + return null; + } } /* Do not change these values without updating their counterparts @@ -3351,73 +3368,209 @@ public class MediaPlayer2 implements AutoCloseable // Modular DRM begin /** - * Interface definition of a callback to be invoked when the app - * can do DRM configuration (get/set properties) before the session - * is opened. This facilitates configuration of the properties, like - * 'securityLevel', which has to be set after DRM scheme creation but - * before the DRM session is opened. + * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM + * protected playback session. * - * The only allowed DRM calls in this listener are - * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)} - * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}. - * @hide + * @see DrmPreparationInfo.Builder */ - public interface OnDrmConfigHelper { + public static final class DrmPreparationInfo { + /** - * Called to give the app the opportunity to configure DRM before the session is created - * - * @param mp the {@code MediaPlayer2} associated with this callback - * @param dsd the DataSourceDesc of this data source + * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object. */ - public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd); - } + public static final class Builder { + + private UUID mUUID; + private byte[] mKeySetId; + private byte[] mInitData; + private String mMimeType; + private int mKeyType; + private Map<String, String> mOptionalParameters; + + /** + * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from + * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}. + * + * @param uuid of selected crypto scheme + * @return this + */ + public Builder setUuid(@NonNull UUID uuid) { + this.mUUID = uuid; + return this; + } - /** - * Register a callback to be invoked for configuration of the DRM object before - * the session is created. - * The callback will be invoked synchronously during the execution - * of {@link #prepareDrm}. - * - * @param listener the callback that will be run - * @hide - */ - // This is a synchronous call. - public void setOnDrmConfigHelper(OnDrmConfigHelper listener) { - mOnDrmConfigHelper = listener; - } + /** + * Set identifier of a persisted offline key obtained from + * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}. + * + * A {@code keySetId} can be used to restore persisted offline keys into a new playback + * session of a DRM protected data source. When {@code keySetId} is set, {@code initData}, + * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored. + * + * @param keySetId identifier of a persisted offline key + * @return this + */ + public Builder setKeySetId(@Nullable byte[] keySetId) { + this.mKeySetId = keySetId; + return this; + } + + /** + * Set container-specific DRM initialization data. Its meaning is interpreted based on + * {@code mimeType}. For example, it could contain the content ID, key ID or other data + * obtained from the content metadata that is required to generate a + * {@link MediaDrm.KeyRequest}. + * + * @param initData container-specific DRM initialization data + * @return this + */ + public Builder setInitData(@Nullable byte[] initData) { + this.mInitData = initData; + return this; + } + + /** + * Set mime type of the content + * + * @param mimeType mime type to the content + * @return this + */ + public Builder setMimeType(@Nullable String mimeType) { + this.mMimeType = mimeType; + return this; + } + + /** + * Set type of the key request. The request may be to acquire keys + * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content, + * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys + * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed. + * + * @param keyType type of the key request + * @return this + */ + public Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) { + this.mKeyType = keyType; + return this; + } + + /** + * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to + * the license server. + * + * @param optionalParameters optional parameters to be included in a key request + * @return this + */ + public Builder setOptionalParameters( + @Nullable Map<String, String> optionalParameters) { + this.mOptionalParameters = optionalParameters; + return this; + } + + /** + * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder + */ + public MediaPlayer2.DrmPreparationInfo build() { + return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType, + mOptionalParameters); + } + + } + + private final UUID mUUID; + private final byte[] mKeySetId; + private final byte[] mInitData; + private final String mMimeType; + private final int mKeyType; + private final Map<String, String> mOptionalParameters; - private OnDrmConfigHelper mOnDrmConfigHelper; + private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType, + int mKeyType, Map<String, String> optionalParameters) { + this.mUUID = mUUID; + this.mKeySetId = mKeySetId; + this.mInitData = mInitData; + this.mMimeType = mMimeType; + this.mKeyType = mKeyType; + this.mOptionalParameters = optionalParameters; + } + + } /** * Interface definition for callbacks to be invoked when the player has the corresponding * DRM events. - * @hide */ public static class DrmEventCallback { /** - * Called to indicate DRM info is available + * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that + * bundles DRM initialization parameters. * * @param mp the {@code MediaPlayer2} associated with this callback - * @param dsd the DataSourceDesc of this data source - * @param drmInfo DRM info of the source including PSSH, and subset - * of crypto schemes supported by this device + * @param dsd the {@link DataSourceDesc} of this data source + * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes + * supported by this device + * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip + * DRM initialization */ - public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { } + public DrmPreparationInfo onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { + return null; + } /** - * Called to notify the client that {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)} - * is finished and ready for key request/response. + * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source + * {@code dsd} * * @param mp the {@code MediaPlayer2} associated with this callback - * @param dsd the DataSourceDesc of this data source + * @param dsd the {@link DataSourceDesc} of this data source * @param status the result of DRM preparation. + * @param keySetId optional identifier that can be used to restore DRM playback initiated + * with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request. + * + * @see DrmPreparationInfo.Builder#setKeySetId(byte[]) + */ + public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { } + + /** + * Called to give the app the opportunity to configure DRM before the session is created. + * + * This facilitates configuration of the properties, like 'securityLevel', which + * has to be set after DRM scheme creation but before the DRM session is opened. + * + * The only allowed DRM calls in this listener are + * {@link MediaDrm#getPropertyString(String)}, + * {@link MediaDrm#getPropertyByteArray(String)}, + * {@link MediaDrm#setPropertyString(String, String)}, + * {@link MediaDrm#setPropertyByteArray(String, byte[])}, + * {@link MediaDrm#setOnExpirationUpdateListener}, + * and {@link MediaDrm#setOnKeyStatusChangeListener}. + * + * @param mp the {@code MediaPlayer2} associated with this callback + * @param dsd the {@link DataSourceDesc} of this data source + * @param drm handle to get/set DRM properties and listeners for this data source */ - public void onDrmPrepared( - MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { } + public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull MediaDrm drm) { } + + /** + * Called to indicate the DRM session for {@code dsd} is ready for key request/response + * + * @param mp the {@code MediaPlayer2} associated with this callback + * @param dsd the {@link DataSourceDesc} of this data source + * @param request a {@link MediaDrm.KeyRequest} prepared using the + * {@link DrmPreparationInfo} returned from + * {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)} + * @return the response to {@code request} (from license server) + */ + public byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull MediaDrm.KeyRequest request) { + return null; + } + } - private final Object mDrmEventCbLock = new Object(); - private ArrayList<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords = + private final Object mDrmEventCallbackLock = new Object(); + private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords = new ArrayList<Pair<Executor, DrmEventCallback>>(); /** @@ -3425,10 +3578,9 @@ public class MediaPlayer2 implements AutoCloseable * * @param eventCallback the callback that will be run * @param executor the executor through which the callback should be invoked - * @hide */ // This is a synchronous call. - public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor, + public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor, @NonNull DrmEventCallback eventCallback) { if (eventCallback == null) { throw new IllegalArgumentException("Illegal null EventCallback"); @@ -3437,8 +3589,9 @@ public class MediaPlayer2 implements AutoCloseable throw new IllegalArgumentException( "Illegal null Executor for the EventCallback"); } - synchronized (mDrmEventCbLock) { - mDrmEventCallbackRecords.add(new Pair(executor, eventCallback)); + synchronized (mDrmEventCallbackLock) { + mDrmEventCallbackRecords = Collections.singletonList( + new Pair<Executor, DrmEventCallback>(executor, eventCallback)); } } @@ -3450,7 +3603,7 @@ public class MediaPlayer2 implements AutoCloseable */ // This is a synchronous call. public void unregisterDrmEventCallback(DrmEventCallback eventCallback) { - synchronized (mDrmEventCbLock) { + synchronized (mDrmEventCallbackLock) { for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { if (cb.second == eventCallback) { mDrmEventCallbackRecords.remove(cb); @@ -3564,7 +3717,7 @@ public class MediaPlayer2 implements AutoCloseable /** * Prepares the DRM for the given data source * <p> - * If {@link OnDrmConfigHelper} is registered, it will be called during + * If {@link DrmEventCallback} is registered, it will be called during * preparation to allow configuration of the DRM properties before opening the * DRM session. It should be used only for a series of * {@link #getDrmPropertyString(DataSourceDesc, String)} and @@ -3587,8 +3740,7 @@ public class MediaPlayer2 implements AutoCloseable * @param dsd The DRM protected data source * * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved - * from the source through {@link #getDrmInfo(DataSourceDesc)} or registering a - * {@link DrmEventCallback#onDrmInfo}. + * from the source listening to {@link DrmEventCallback#onDrmInfo}. * * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. * @hide @@ -3661,8 +3813,8 @@ public class MediaPlayer2 implements AutoCloseable sendDrmEvent(new DrmEventNotifier() { @Override public void notify(DrmEventCallback callback) { - callback.onDrmPrepared( - MediaPlayer2.this, dsd, prepareDrmStatus); + callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus, + /* TODO: keySetId */ null); } }); @@ -3876,7 +4028,6 @@ public class MediaPlayer2 implements AutoCloseable /** * Encapsulates the DRM properties of the source. - * @hide */ public static final class DrmInfo { private Map<UUID, byte[]> mMapPssh; @@ -4013,10 +4164,8 @@ public class MediaPlayer2 implements AutoCloseable }; // DrmInfo /** - * Thrown when a DRM method is called before preparing a DRM scheme through - * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}. + * Thrown when a DRM method is called when there is no active DRM session. * Extends MediaDrm.MediaDrmException - * @hide */ public static final class NoDrmSchemeException extends MediaDrmException { public NoDrmSchemeException(String detailMessage) { @@ -4291,9 +4440,9 @@ public class MediaPlayer2 implements AutoCloseable } void prepare(UUID uuid) throws UnsupportedSchemeException, - ResourceBusyException, NotProvisionedException { - final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper; - Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper); + ResourceBusyException, NotProvisionedException, InterruptedException, + ExecutionException { + Log.v(TAG, "prepareDrm: uuid: " + uuid); synchronized (this) { if (mActiveDrmUUID != null) { @@ -4334,9 +4483,13 @@ public class MediaPlayer2 implements AutoCloseable } // synchronized // call the callback outside the lock - if (onDrmConfigHelper != null) { - onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD); - } + sendDrmEventWait(new DrmEventNotifier<Void>() { + @Override + public Void notifyWait(DrmEventCallback callback) { + callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj); + return null; + } + }); synchronized (this) { mDrmConfigAllowed = false; @@ -4506,7 +4659,7 @@ public class MediaPlayer2 implements AutoCloseable @Override public void notify(DrmEventCallback callback) { callback.onDrmPrepared( - MediaPlayer2.this, mDSD, finalStatus); + MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null); } }); diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 25b1df26fd40..1304afe3654e 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -302,6 +302,34 @@ public class MediaRecorder implements AudioRouting, * {@link #DEFAULT} otherwise. */ public static final int UNPROCESSED = 9; + + /** + * Source for capturing audio meant to be processed in real time and played back for live + * performance (e.g karaoke). + * <p> + * The capture path will minimize latency and coupling with + * playback path. + * </p> + */ + public static final int VOICE_PERFORMANCE = 10; + + /** + * Source for an echo canceller to capture the reference signal to be cancelled. + * <p> + * The echo reference signal will be captured as close as possible to the DAC in order + * to include all post processing applied to the playback path. + * </p><p> + * Capturing the echo reference requires the + * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission. + * This permission is reserved for use by system components and is not available to + * third-party applications. + * </p> + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) + public static final int ECHO_REFERENCE = 1997; + /** * Audio source for capturing broadcast radio tuner output. * @hide @@ -343,6 +371,7 @@ public class MediaRecorder implements AudioRouting, case AudioSource.VOICE_COMMUNICATION: //case REMOTE_SUBMIX: considered "system" as it requires system permissions case AudioSource.UNPROCESSED: + case AudioSource.VOICE_PERFORMANCE: return false; default: return true; @@ -372,6 +401,10 @@ public class MediaRecorder implements AudioRouting, return "REMOTE_SUBMIX"; case AudioSource.UNPROCESSED: return "UNPROCESSED"; + case AudioSource.ECHO_REFERENCE: + return "ECHO_REFERENCE"; + case AudioSource.VOICE_PERFORMANCE: + return "VOICE_PERFORMANCE"; case AudioSource.RADIO_TUNER: return "RADIO_TUNER"; case AudioSource.HOTWORD: @@ -524,7 +557,7 @@ public class MediaRecorder implements AudioRouting, * @see android.media.MediaRecorder.AudioSource */ public static final int getAudioSourceMax() { - return AudioSource.UNPROCESSED; + return AudioSource.VOICE_PERFORMANCE; } /** diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java index e008adf120ab..04999cace5e0 100644 --- a/media/java/android/media/MediaSession2.java +++ b/media/java/android/media/MediaSession2.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.Intent; import android.media.session.MediaSessionManager; import android.media.session.MediaSessionManager.RemoteUserInfo; -import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Process; @@ -41,7 +40,6 @@ import android.util.ArraySet; import android.util.Log; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -117,14 +115,18 @@ public class MediaSession2 implements AutoCloseable { @Override public void close() { try { - synchronized (MediaSession2.class) { - SESSION_ID_LIST.remove(mSessionId); - } - Collection<ControllerInfo> controllerInfos; + List<ControllerInfo> controllerInfos; synchronized (mLock) { - controllerInfos = mConnectedControllers.values(); - mConnectedControllers.clear(); + if (mClosed) { + return; + } mClosed = true; + controllerInfos = getConnectedControllers(); + mConnectedControllers.clear(); + mCallback.onSessionClosed(this); + } + synchronized (MediaSession2.class) { + SESSION_ID_LIST.remove(mSessionId); } for (ControllerInfo info : controllerInfos) { info.notifyDisconnected(); @@ -160,10 +162,7 @@ public class MediaSession2 implements AutoCloseable { if (command == null) { throw new IllegalArgumentException("command shouldn't be null"); } - Collection<ControllerInfo> controllerInfos; - synchronized (mLock) { - controllerInfos = mConnectedControllers.values(); - } + List<ControllerInfo> controllerInfos = getConnectedControllers(); for (ControllerInfo controller : controllerInfos) { controller.sendSessionCommand(command, args, null); } @@ -222,23 +221,26 @@ public class MediaSession2 implements AutoCloseable { } } - // Called by Session2Link.onConnect - void onConnect(final Controller2Link controller, int seq, Bundle connectionRequest) { - if (controller == null || connectionRequest == null) { - return; - } - final int uid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final long token = Binder.clearCallingIdentity(); - // Binder.getCallingPid() can be 0 for an oneway call from the remote process. - // If it's the case, use PID from the ConnectionRequest. - final int pid = (callingPid != 0) ? callingPid : connectionRequest.getInt(KEY_PID); - final String pkg = connectionRequest.getString(KEY_PACKAGE_NAME); - try { - RemoteUserInfo remoteUserInfo = new RemoteUserInfo(pkg, pid, uid); - final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo, - mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller); - mCallbackExecutor.execute(() -> { + SessionCallback getCallback() { + return mCallback; + } + + // Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect + void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq, + Bundle connectionRequest) { + if (callingPid == 0) { + // The pid here is from Binder.getCallingPid(), which can be 0 for an oneway call from + // the remote process. If it's the case, use PID from the connectionRequest. + callingPid = connectionRequest.getInt(KEY_PID); + } + String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME); + + RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid); + final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo, + mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller); + mCallbackExecutor.execute(() -> { + boolean connected = false; + try { if (isClosed()) { return; } @@ -247,77 +249,71 @@ public class MediaSession2 implements AutoCloseable { // Don't reject connection for the request from trusted app. // Otherwise server will fail to retrieve session's information to dispatch // media keys to. - boolean accept = - controllerInfo.mAllowedCommands != null || controllerInfo.isTrusted(); - if (accept) { - if (controllerInfo.mAllowedCommands == null) { - // For trusted apps, send non-null allowed commands to keep - // connection. - controllerInfo.mAllowedCommands = - new Session2CommandGroup.Builder().build(); + if (controllerInfo.mAllowedCommands == null && !controllerInfo.isTrusted()) { + return; + } + if (controllerInfo.mAllowedCommands == null) { + // For trusted apps, send non-null allowed commands to keep + // connection. + controllerInfo.mAllowedCommands = + new Session2CommandGroup.Builder().build(); + } + if (DEBUG) { + Log.d(TAG, "Accepting connection: " + controllerInfo); + } + synchronized (mLock) { + if (mConnectedControllers.containsKey(controller)) { + Log.w(TAG, "Controller " + controllerInfo + " has sent connection" + + " request multiple times"); } + mConnectedControllers.put(controller, controllerInfo); + } + // If connection is accepted, notify the current state to the controller. + // It's needed because we cannot call synchronous calls between + // session/controller. + Bundle connectionResult = new Bundle(); + connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub); + connectionResult.putParcelable(KEY_ALLOWED_COMMANDS, + controllerInfo.mAllowedCommands); + + // Double check if session is still there, because close() can be called in + // another thread. + if (isClosed()) { + return; + } + controllerInfo.notifyConnected(connectionResult); + connected = true; + } finally { + if (!connected) { if (DEBUG) { - Log.d(TAG, "Accepting connection: " + controllerInfo); + Log.d(TAG, "Rejecting connection or notifying that session is closed" + + ", controllerInfo=" + controllerInfo); } synchronized (mLock) { - if (mConnectedControllers.containsKey(controller)) { - Log.w(TAG, "Controller " + controllerInfo + " has sent connection" - + " request multiple times"); - } - mConnectedControllers.put(controller, controllerInfo); - } - // If connection is accepted, notify the current state to the controller. - // It's needed because we cannot call synchronous calls between - // session/controller. - Bundle connectionResult = new Bundle(); - connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub); - connectionResult.putParcelable(KEY_ALLOWED_COMMANDS, - controllerInfo.mAllowedCommands); - - // Double check if session is still there, because close() can be called in - // another thread. - if (isClosed()) { - return; - } - controllerInfo.notifyConnected(connectionResult); - } else { - if (DEBUG) { - Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo); + mConnectedControllers.remove(controller); } controllerInfo.notifyDisconnected(); } - }); - } finally { - Binder.restoreCallingIdentity(token); - } + } + }); } // Called by Session2Link.onDisconnect - void onDisconnect(final Controller2Link controller, int seq) { - if (controller == null) { - return; - } + void onDisconnect(@NonNull final Controller2Link controller, int seq) { final ControllerInfo controllerInfo; synchronized (mLock) { - controllerInfo = mConnectedControllers.get(controller); + controllerInfo = mConnectedControllers.remove(controller); } if (controllerInfo == null) { return; } - - final long token = Binder.clearCallingIdentity(); - try { - mCallbackExecutor.execute(() -> { - mCallback.onDisconnected(MediaSession2.this, controllerInfo); - }); - mConnectedControllers.remove(controller); - } finally { - Binder.restoreCallingIdentity(token); - } + mCallbackExecutor.execute(() -> { + mCallback.onDisconnected(MediaSession2.this, controllerInfo); + }); } // Called by Session2Link.onSessionCommand - void onSessionCommand(final Controller2Link controller, final int seq, + void onSessionCommand(@NonNull final Controller2Link controller, final int seq, final Session2Command command, final Bundle args, @Nullable ResultReceiver resultReceiver) { if (controller == null) { @@ -332,34 +328,28 @@ public class MediaSession2 implements AutoCloseable { } // TODO: check allowed commands. - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - controllerInfo.addRequestedCommandSeqNumber(seq); + synchronized (mLock) { + controllerInfo.addRequestedCommandSeqNumber(seq); + } + mCallbackExecutor.execute(() -> { + if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) { + resultReceiver.send(RESULT_INFO_SKIPPED, null); + return; } - - mCallbackExecutor.execute(() -> { - if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); - return; - } - Session2Command.Result result = mCallback.onSessionCommand( - MediaSession2.this, controllerInfo, command, args); - if (resultReceiver != null) { - if (result == null) { - throw new RuntimeException("onSessionCommand shouldn't return null"); - } else { - resultReceiver.send(result.getResultCode(), result.getResultData()); - } + Session2Command.Result result = mCallback.onSessionCommand( + MediaSession2.this, controllerInfo, command, args); + if (resultReceiver != null) { + if (result == null) { + throw new RuntimeException("onSessionCommand shouldn't return null"); + } else { + resultReceiver.send(result.getResultCode(), result.getResultData()); } - }); - } finally { - Binder.restoreCallingIdentity(token); - } + } + }); } // Called by Session2Link.onCancelCommand - void onCancelCommand(final Controller2Link controller, final int seq) { + void onCancelCommand(@NonNull final Controller2Link controller, final int seq) { final ControllerInfo controllerInfo; synchronized (mLock) { controllerInfo = mConnectedControllers.get(controller); @@ -367,13 +357,15 @@ public class MediaSession2 implements AutoCloseable { if (controllerInfo == null) { return; } + controllerInfo.removeRequestedCommandSeqNumber(seq); + } - final long token = Binder.clearCallingIdentity(); - try { - controllerInfo.removeRequestedCommandSeqNumber(seq); - } finally { - Binder.restoreCallingIdentity(token); + private List<ControllerInfo> getConnectedControllers() { + List<ControllerInfo> controllers = new ArrayList<>(); + synchronized (mLock) { + controllers.addAll(mConnectedControllers.values()); } + return controllers; } /** @@ -660,6 +652,8 @@ public class MediaSession2 implements AutoCloseable { * This API is not generally intended for third party application developers. */ public abstract static class SessionCallback { + ForegroundServiceEventCallback mForegroundServiceEventCallback; + /** * Called when a controller is created for this session. Return allowed commands for * controller. By default it returns {@code null}. @@ -716,5 +710,19 @@ public class MediaSession2 implements AutoCloseable { public void onCommandResult(@NonNull MediaSession2 session, @NonNull ControllerInfo controller, @NonNull Object token, @NonNull Session2Command command, @NonNull Session2Command.Result result) {} + + final void onSessionClosed(MediaSession2 session) { + if (mForegroundServiceEventCallback != null) { + mForegroundServiceEventCallback.onSessionClosed(session); + } + } + + void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) { + mForegroundServiceEventCallback = callback; + } + + abstract static class ForegroundServiceEventCallback { + public void onSessionClosed(MediaSession2 session) {} + } } } diff --git a/media/java/android/media/MediaSession2Service.java b/media/java/android/media/MediaSession2Service.java new file mode 100644 index 000000000000..8fb00fe487e8 --- /dev/null +++ b/media/java/android/media/MediaSession2Service.java @@ -0,0 +1,288 @@ +/* + * 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.media; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Service containing {@link MediaSession2}. + * <p> + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> + * for consistent behavior across all devices. + * @hide + */ +// TODO: Unhide +// TODO: Add onUpdateNotification(), and calls it to get Notification for startForegroundService() +// when a session's player state becomes playing. +public abstract class MediaSession2Service extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. + */ + public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service"; + + private static final String TAG = "MediaSession2Service"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final Object mLock = new Object(); + @GuardedBy("mLock") + private Map<String, MediaSession2> mSessions = new ArrayMap<>(); + + private MediaSession2ServiceStub mStub; + + /** + * Called by the system when the service is first created. Do not call this method directly. + * <p> + * Override this method if you need your own initialization. Derived classes MUST call through + * to the super class's implementation of this method. + */ + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + mStub = new MediaSession2ServiceStub(this); + } + + @CallSuper + @Override + @Nullable + public IBinder onBind(@NonNull Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mStub; + } + return null; + } + + @CallSuper + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // TODO: Dispatch media key events to the primary session. + return START_STICKY; + } + + /** + * Called by the system to notify that it is no longer used and is being removed. Do not call + * this method directly. + * <p> + * Override this method if you need your own clean up. Derived classes MUST call through + * to the super class's implementation of this method. + */ + @CallSuper + @Override + public void onDestroy() { + super.onDestroy(); + synchronized (mLock) { + for (MediaSession2 session : mSessions.values()) { + session.getCallback().setForegroundServiceEventCallback(null); + } + mSessions.clear(); + } + mStub.close(); + } + + /** + * Called when a {@link MediaController2} is created with the this service's + * {@link Session2Token}. Return the primary session for telling the controller which session to + * connect. + * <p> + * Primary session is the highest priority session that this service manages. Here are some + * recommendations of the primary session. + * <ol> + * <li>When there's no {@link MediaSession2}, create and return a new session. Resume the + * playback that the app has the lastly played with the new session. The behavior is what + * framework expects when the framework sends key events to the service.</li> + * <li>When there's multiple {@link MediaSession2}s, pick the session that has the lastly + * started the playback. This is the same way as the framework prioritize sessions to receive + * media key events.</li> + * </ol> + * <p> + * Session returned here will be added to this service automatically. You don't need to call + * {@link #addSession(MediaSession2)} for that. + * <p> + * Session service will accept or reject the connection with the + * {@link MediaSession2.SessionCallback} in the session returned here. + * <p> + * This method is always called on the main thread. + * + * @return a new session + * @see MediaSession2.Builder + * @see #getSessions() + */ + @NonNull + public abstract MediaSession2 onGetPrimarySession(); + + /** + * Adds a session to this service. + * <p> + * Added session will be removed automatically when it's closed, or removed when + * {@link #removeSession} is called. + * + * @param session a session to be added. + * @see #removeSession(MediaSession2) + */ + public final void addSession(@NonNull MediaSession2 session) { + if (session == null) { + throw new IllegalArgumentException("session shouldn't be null"); + } + if (session.isClosed()) { + throw new IllegalArgumentException("session is already closed"); + } + synchronized (mLock) { + MediaSession2 previousSession = mSessions.get(session.getSessionId()); + if (previousSession != session) { + if (previousSession != null) { + Log.w(TAG, "Session ID should be unique, ID=" + session.getSessionId() + + ", previous=" + previousSession + ", session=" + session); + } + return; + } + mSessions.put(session.getSessionId(), session); + session.getCallback().setForegroundServiceEventCallback( + new MediaSession2.SessionCallback.ForegroundServiceEventCallback() { + @Override + public void onSessionClosed(MediaSession2 session) { + removeSession(session); + } + }); + } + } + + /** + * Removes a session from this service. + * + * @param session a session to be removed. + * @see #addSession(MediaSession2) + */ + public final void removeSession(@NonNull MediaSession2 session) { + if (session == null) { + throw new IllegalArgumentException("session shouldn't be null"); + } + synchronized (mLock) { + mSessions.remove(session.getSessionId()); + } + } + + /** + * Gets the list of {@link MediaSession2}s that you've added to this service. + * + * @return sessions + */ + public final @NonNull List<MediaSession2> getSessions() { + List<MediaSession2> list = new ArrayList<>(); + synchronized (mLock) { + list.addAll(mSessions.values()); + } + return list; + } + + private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub + implements AutoCloseable { + final WeakReference<MediaSession2Service> mService; + final Handler mHandler; + + MediaSession2ServiceStub(MediaSession2Service service) { + mService = new WeakReference<>(service); + mHandler = new Handler(service.getMainLooper()); + } + + @Override + public void connect(Controller2Link caller, int seq, Bundle connectionRequest) { + if (mService.get() == null) { + if (DEBUG) { + Log.d(TAG, "Service is already destroyed"); + } + return; + } + if (caller == null || connectionRequest == null) { + if (DEBUG) { + Log.d(TAG, "Ignoring calls with illegal arguments, caller=" + caller + + ", connectionRequest=" + connectionRequest); + } + return; + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + mHandler.post(() -> { + boolean shouldNotifyDisconnected = true; + try { + final MediaSession2Service service = mService.get(); + if (service == null) { + if (DEBUG) { + Log.d(TAG, "Service isn't available"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "Handling incoming connection request from the" + + " controller, controller=" + caller + ", uid=" + uid); + } + final MediaSession2 session; + session = service.onGetPrimarySession(); + service.addSession(session); + shouldNotifyDisconnected = false; + session.onConnect(caller, pid, uid, seq, connectionRequest); + } catch (Exception e) { + // Don't propagate exception in service to the controller. + Log.w(TAG, "Failed to add a session to session service", e); + } finally { + // Trick to call onDisconnected() in one place. + if (shouldNotifyDisconnected) { + if (DEBUG) { + Log.d(TAG, "Service has destroyed prematurely." + + " Rejecting connection"); + } + try { + caller.notifyDisconnected(0); + } catch (RuntimeException e) { + // Controller may be died prematurely. + // Not an issue because we'll ignore it anyway. + } + } + } + }); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void close() { + mHandler.removeCallbacksAndMessages(null); + mService.clear(); + } + } +} diff --git a/media/java/android/media/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java new file mode 100644 index 000000000000..99201c0279bf --- /dev/null +++ b/media/java/android/media/MicrophoneDirection.java @@ -0,0 +1,62 @@ +/* + * 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.media; + +/** + * @hide + */ +public interface MicrophoneDirection { + /** + * @hide + */ + int MIC_DIRECTION_UNSPECIFIED = 0; + + /** + * @hide + */ + int MIC_DIRECTION_FRONT = 1; + + /** + * @hide + */ + int MIC_DIRECTION_BACK = 2; + + /** + * @hide + */ + int MIC_DIRECTION_EXTERNAL = 3; + + /** + * Specifies the logical microphone (for processing). + * + * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*) + * @return retval OK if the call is successful, an error code otherwise. + * @hide + */ + int setMicrophoneDirection(int direction); + + /** + * Specifies the zoom factor (i.e. the field dimension) for the selected microphone + * (for processing). The selected microphone is determined by the use-case for the stream. + * + * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle), + * though 0 (no zoom) to 1 (maximum zoom). + * @return retval OK if the call is successful, an error code otherwise. + * @hide + */ + int setMicrophoneFieldDimension(float zoom); +} diff --git a/media/java/android/media/Session2Link.java b/media/java/android/media/Session2Link.java index 5fe558da12f5..08664aa3b38f 100644 --- a/media/java/android/media/Session2Link.java +++ b/media/java/android/media/Session2Link.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.NonNull; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; @@ -145,8 +146,9 @@ public final class Session2Link implements Parcelable { } /** Stub implementation for IMediaSession2.connect */ - public void onConnect(final Controller2Link caller, int seq, Bundle connectionRequest) { - mSession.onConnect(caller, seq, connectionRequest); + public void onConnect(final Controller2Link caller, int pid, int uid, int seq, + Bundle connectionRequest) { + mSession.onConnect(caller, pid, uid, seq, connectionRequest); } /** Stub implementation for IMediaSession2.disconnect */ @@ -168,23 +170,57 @@ public final class Session2Link implements Parcelable { private class Session2Stub extends IMediaSession2.Stub { @Override public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) { - Session2Link.this.onConnect(caller, seq, connectionRequest); + if (caller == null || connectionRequest == null) { + return; + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + Session2Link.this.onConnect(caller, pid, uid, seq, connectionRequest); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override public void disconnect(final Controller2Link caller, int seq) { - Session2Link.this.onDisconnect(caller, seq); + if (caller == null) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + Session2Link.this.onDisconnect(caller, seq); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override public void sendSessionCommand(final Controller2Link caller, final int seq, final Session2Command command, final Bundle args, ResultReceiver resultReceiver) { - Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver); + if (caller == null) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override public void cancelSessionCommand(final Controller2Link caller, final int seq) { - Session2Link.this.onCancelCommand(caller, seq); + if (caller == null) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + Session2Link.this.onCancelCommand(caller, seq); + } finally { + Binder.restoreCallingIdentity(token); + } } } } diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java index 95ee4c039f4b..c7b891151201 100644 --- a/media/java/android/media/Session2Token.java +++ b/media/java/android/media/Session2Token.java @@ -152,9 +152,7 @@ public final class Session2Token implements Parcelable { mType = in.readInt(); mPackageName = in.readString(); mServiceName = in.readString(); - // TODO: Uncomment below and stop hardcode mSessionLink - mSessionLink = null; - //mSessionLink = ISession.Stub.asInterface(in.readStrongBinder()); + mSessionLink = Session2Link.CREATOR.createFromParcel(in); mComponentName = ComponentName.unflattenFromString(in.readString()); } @@ -164,8 +162,7 @@ public final class Session2Token implements Parcelable { dest.writeInt(mType); dest.writeString(mPackageName); dest.writeString(mServiceName); - // TODO: Uncomment below - //dest.writeStrongBinder(mSessionLink.getBinder()); + mSessionLink.writeToParcel(dest, flags); dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString()); } diff --git a/media/java/android/media/session/MediaSessionProviderService.java b/media/java/android/media/session/MediaSessionProviderService.java new file mode 100644 index 000000000000..9a346ff4a12e --- /dev/null +++ b/media/java/android/media/session/MediaSessionProviderService.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.media.session; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +/** + * Abstract class for mainline module services. + * + * @hide // TODO: Make it as a @SystemApi + */ +public abstract class MediaSessionProviderService extends Service { + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return IMediaSessionProviderService.Stub() + return null; + } +} diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 452c6e1becca..01984709f514 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -84,7 +84,7 @@ cc_library_shared { } cc_library_shared { - name: "libmedia2_jni", + name: "libmediaplayer2_jni", srcs: [ "android_media_DataSourceCallback.cpp", @@ -116,7 +116,10 @@ cc_library_shared { "libz", ], - header_libs: ["libhardware_headers"], + header_libs: [ + "libhardware_headers", + "libnativewindow_headers", + ], static_libs: [ "libbase", diff --git a/media/jni/Android.mk b/media/jni/Android.mk new file mode 100644 index 000000000000..1d7d6de27115 --- /dev/null +++ b/media/jni/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Wall -Werror +LOCAL_SRC_FILES := \ + Media2Jni.cpp \ + +# TODO: Move libmedia2_jni from system to media apex. Currently, libraries defined in +# Android.mk is not visible in apex build. +LOCAL_MODULE:= libmedia2_jni +LOCAL_SHARED_LIBRARIES := libdl liblog + +sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\ + $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ + $(UBSAN_RUNTIME_LIBRARY) \ + $(TSAN_RUNTIME_LIBRARY))) + +# $(info Sanitizer: $(sanitizer_runtime_libraries)) + +ndk_libraries := $(call normalize-path-list,$(addprefix lib,$(addsuffix .so,\ + $(NDK_PREBUILT_SHARED_LIBRARIES)))) + +# $(info NDK: $(ndk_libraries)) + +LOCAL_CFLAGS += -DLINKED_LIBRARIES='"$(sanitizer_runtime_libraries):$(ndk_libraries)"' + +sanitizer_runtime_libraries := +ndk_libraries := + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/jni/Media2Jni.cpp b/media/jni/Media2Jni.cpp new file mode 100644 index 000000000000..6c0a65cba6b5 --- /dev/null +++ b/media/jni/Media2Jni.cpp @@ -0,0 +1,89 @@ +/* +** +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaPlayer2-JNI" +#include "utils/Log.h" + +#include "jni.h" +#include <android/dlext.h> +#include <dirent.h> +#include <dlfcn.h> +#include <errno.h> +#include <string.h> + +extern "C" { + // Copied from GraphicsEnv.cpp + // TODO(b/37049319) Get this from a header once one exists + android_namespace_t* android_create_namespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent); + bool android_link_namespaces(android_namespace_t* from, + android_namespace_t* to, + const char* shared_libs_sonames); + enum { + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + }; + +} // extern "C" + +static const char kApexLibPath[] = "/apex/com.android.media/lib" +#ifdef __LP64__ + "64" +#endif + "/"; +static const char kMediaPlayer2LibPath[] = "/apex/com.android.media/lib" +#ifdef __LP64__ + "64" +#endif + "/libmediaplayer2_jni.so"; + +typedef jint (*Media2JniOnLoad)(JavaVM*, void*); + +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + android_namespace_t *media2Ns = android_create_namespace("media2", + nullptr, // ld_library_path + kApexLibPath, + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); // parent + if (!android_link_namespaces(media2Ns, nullptr, LINKED_LIBRARIES)) { + ALOGE("Failed to link namespace. Failed to load extractor plug-ins in apex."); + return -1; + } + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = media2Ns, + }; + // load libmediaplayer2_jni and call JNI_OnLoad. + void *libHandle = android_dlopen_ext(kMediaPlayer2LibPath, RTLD_NOW | RTLD_LOCAL, &dlextinfo); + if (libHandle == NULL) { + ALOGW("couldn't dlopen(%s) %s", kMediaPlayer2LibPath, strerror(errno)); + return -1; + } + Media2JniOnLoad media2JniOnLoad = (Media2JniOnLoad) dlsym(libHandle, "JNI_OnLoad"); + if (!media2JniOnLoad) { + ALOGW("%s does not contain JNI_OnLoad()", kMediaPlayer2LibPath); + dlclose(libHandle); + return -1; + } + return media2JniOnLoad(vm, reserved); +} diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 7b07bea3cf1a..406f9dd94bb4 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -648,7 +648,6 @@ static jobject getCodecCapabilitiesObject( capabilities->getSupportedColorFormats(&colorFormats); capabilities->getSupportedProfileLevels(&profileLevels); - uint32_t flags = capabilities->getFlags(); sp<AMessage> details = capabilities->getDetails(); jobject defaultFormatObj = NULL; @@ -687,7 +686,7 @@ static jobject getCodecCapabilitiesObject( return env->NewObject( gCodecInfo.capsClazz, gCodecInfo.capsCtorId, - profileLevelArray.get(), colorFormatsArray.get(), isEncoder, flags, + profileLevelArray.get(), colorFormatsArray.get(), isEncoder, defaultFormatRef.get(), detailsRef.get()); } @@ -700,23 +699,28 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const return err; } + // TODO: get alias ScopedLocalRef<jstring> nameObject(env, env->NewStringUTF(codecInfo->getCodecName())); + ScopedLocalRef<jstring> canonicalNameObject(env, + env->NewStringUTF(codecInfo->getCodecName())); + + MediaCodecInfo::Attributes attributes = codecInfo->getAttributes(); bool isEncoder = codecInfo->isEncoder(); - Vector<AString> mimes; - codecInfo->getSupportedMimes(&mimes); + Vector<AString> mediaTypes; + codecInfo->getSupportedMediaTypes(&mediaTypes); ScopedLocalRef<jobjectArray> capsArrayObj(env, - env->NewObjectArray(mimes.size(), gCodecInfo.capsClazz, NULL)); + env->NewObjectArray(mediaTypes.size(), gCodecInfo.capsClazz, NULL)); - for (size_t i = 0; i < mimes.size(); i++) { + for (size_t i = 0; i < mediaTypes.size(); i++) { const sp<MediaCodecInfo::Capabilities> caps = - codecInfo->getCapabilitiesFor(mimes[i].c_str()); + codecInfo->getCapabilitiesFor(mediaTypes[i].c_str()); ScopedLocalRef<jobject> capsObj(env, getCodecCapabilitiesObject( - env, mimes[i].c_str(), isEncoder, caps)); + env, mediaTypes[i].c_str(), isEncoder, caps)); env->SetObjectArrayElement(capsArrayObj.get(), i, capsObj.get()); } @@ -726,10 +730,10 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const CHECK(codecInfoClazz.get() != NULL); jmethodID codecInfoCtorID = env->GetMethodID(codecInfoClazz.get(), "<init>", - "(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V"); + "(Ljava/lang/String;Ljava/lang/String;I[Landroid/media/MediaCodecInfo$CodecCapabilities;)V"); *codecInfoObject = env->NewObject(codecInfoClazz.get(), codecInfoCtorID, - nameObject.get(), isEncoder, capsArrayObj.get()); + nameObject.get(), canonicalNameObject.get(), attributes, capsArrayObj.get()); return OK; } @@ -2079,7 +2083,7 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { gCodecInfo.capsClazz = (jclass)env->NewGlobalRef(clazz.get()); method = env->GetMethodID(clazz.get(), "<init>", - "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZI" + "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ" "Ljava/util/Map;Ljava/util/Map;)V"); CHECK(method != NULL); gCodecInfo.capsCtorId = method; @@ -2217,7 +2221,7 @@ static const JNINativeMethod gMethods[] = { { "getImage", "(ZI)Landroid/media/Image;", (void *)android_media_MediaCodec_getImage }, - { "getName", "()Ljava/lang/String;", + { "getCanonicalName", "()Ljava/lang/String;", (void *)android_media_MediaCodec_getName }, { "getOwnCodecInfo", "()Landroid/media/MediaCodecInfo;", diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index 8de11caf7d7a..cf1494296756 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -41,6 +41,21 @@ static sp<IMediaCodecList> getCodecList(JNIEnv *env) { return mcl; } +static sp<MediaCodecInfo> getCodecInfo(JNIEnv *env, jint index) { + sp<IMediaCodecList> mcl = getCodecList(env); + if (mcl == NULL) { + // Runtime exception already pending. + return NULL; + } + + sp<MediaCodecInfo> info = mcl->getCodecInfo(index); + if (info == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + } + + return info; +} + static jint android_media_MediaCodecList_getCodecCount( JNIEnv *env, jobject /* thiz */) { sp<IMediaCodecList> mcl = getCodecList(env); @@ -53,15 +68,22 @@ static jint android_media_MediaCodecList_getCodecCount( static jstring android_media_MediaCodecList_getCodecName( JNIEnv *env, jobject /* thiz */, jint index) { - sp<IMediaCodecList> mcl = getCodecList(env); - if (mcl == NULL) { + sp<MediaCodecInfo> info = getCodecInfo(env, index); + if (info == NULL) { // Runtime exception already pending. return NULL; } - const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index); + // TODO: support aliases + const char *name = info->getCodecName(); + 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) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + // Runtime exception already pending. return NULL; } @@ -94,39 +116,27 @@ static jint android_media_MediaCodecList_findCodecByName( return ret; } -static jboolean android_media_MediaCodecList_isEncoder( +static jboolean android_media_MediaCodecList_getAttributes( JNIEnv *env, jobject /* thiz */, jint index) { - sp<IMediaCodecList> mcl = getCodecList(env); - if (mcl == NULL) { - // Runtime exception already pending. - return false; - } - - const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index); + sp<MediaCodecInfo> info = getCodecInfo(env, index); if (info == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return false; + // Runtime exception already pending. + return 0; } - return info->isEncoder(); + return info->getAttributes(); } static jarray android_media_MediaCodecList_getSupportedTypes( JNIEnv *env, jobject /* thiz */, jint index) { - sp<IMediaCodecList> mcl = getCodecList(env); - if (mcl == NULL) { - // Runtime exception already pending. - return NULL; - } - - const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index); + sp<MediaCodecInfo> info = getCodecInfo(env, index); if (info == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + // Runtime exception already pending. return NULL; } Vector<AString> types; - info->getSupportedMimes(&types); + info->getSupportedMediaTypes(&types); jclass clazz = env->FindClass("java/lang/String"); CHECK(clazz != NULL); @@ -150,17 +160,12 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( return NULL; } - sp<IMediaCodecList> mcl = getCodecList(env); - if (mcl == NULL) { + sp<MediaCodecInfo> info = getCodecInfo(env, index); + if (info == NULL) { // Runtime exception already pending. return NULL; } - const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index); - if (info == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return NULL; - } const char *typeStr = env->GetStringUTFChars(type, NULL); if (typeStr == NULL) { @@ -186,7 +191,6 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( capabilities->getSupportedColorFormats(&colorFormats); capabilities->getSupportedProfileLevels(&profileLevels); - uint32_t flags = capabilities->getFlags(); sp<AMessage> details = capabilities->getDetails(); bool isEncoder = info->isEncoder(); @@ -240,11 +244,11 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( } jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>", - "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZI" + "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ" "Ljava/util/Map;Ljava/util/Map;)V"); jobject caps = env->NewObject(capsClazz, capsConstructID, - profileLevelArray, colorFormatsArray, isEncoder, flags, + profileLevelArray, colorFormatsArray, isEncoder, defaultFormatObj, infoObj); env->DeleteLocalRef(profileLevelArray); @@ -288,9 +292,15 @@ static void android_media_MediaCodecList_native_init(JNIEnv* /* env */) { static const JNINativeMethod gMethods[] = { { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount }, + + { "getCanonicalName", "(I)Ljava/lang/String;", + (void *)android_media_MediaCodecList_getCanonicalName }, + { "getCodecName", "(I)Ljava/lang/String;", (void *)android_media_MediaCodecList_getCodecName }, - { "isEncoder", "(I)Z", (void *)android_media_MediaCodecList_isEncoder }, + + { "getAttributes", "(I)I", (void *)android_media_MediaCodecList_getAttributes }, + { "getSupportedTypes", "(I)[Ljava/lang/String;", (void *)android_media_MediaCodecList_getSupportedTypes }, diff --git a/media/packages/MediaCore/Android.bp b/media/packages/MediaCore/Android.bp new file mode 100644 index 000000000000..c7fd58bf933a --- /dev/null +++ b/media/packages/MediaCore/Android.bp @@ -0,0 +1,21 @@ +android_app { + name: "MediaCore", + + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + // TODO: Temporarily statically linked. Should go into "libs" + "media1", + ], + + // System app + platform_apis: true, + + // Privileged app + privileged: true, + + // Make sure that the implementation only relies on SDK or system APIs. + sdk_version: "system_current", +} diff --git a/media/packages/MediaCore/AndroidManifest.xml b/media/packages/MediaCore/AndroidManifest.xml new file mode 100644 index 000000000000..4e2b274511e8 --- /dev/null +++ b/media/packages/MediaCore/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/AndroidManifest.xml +** +** 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. +*/ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.media" coreApp="true" android:sharedUserId="android.uid.system" + android:sharedUserLabel="@string/android_system_label"> + <application android:process="system" + android:persistent="true" + android:directBootAware="true"> + <service android:name="AmlMediaSessionProviderService" android:singleUser="true"> + <intent-filter> + <action android:name="android.media.session.MediaSessionProviderService"/> + </intent-filter> + </service> + </application> +</manifest> diff --git a/media/packages/MediaCore/res/values/strings.xml b/media/packages/MediaCore/res/values/strings.xml new file mode 100644 index 000000000000..59fd635b905f --- /dev/null +++ b/media/packages/MediaCore/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- Label for the Android system components when they are shown to the user. --> + <string name="android_system_label" translatable="false">Android System</string> +</resources> + diff --git a/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java new file mode 100644 index 000000000000..43b95ab7ebdb --- /dev/null +++ b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java @@ -0,0 +1,37 @@ +/* + * 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 com.android.media; + +import android.content.Context; +import android.media.session.MediaSessionProviderService; +import android.os.PowerManager; +import android.util.Log; + +/** + * System implementation of MediaSessionProviderService + */ +public class AmlMediaSessionProviderService extends MediaSessionProviderService { + private static final String TAG = "AmlMediaSessionProviderS"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private Context mContext; + + public AmlMediaSessionProviderService(Context context) { + mContext = context; + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + } +} diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index 8e5821024cee..c3b2e2526ea8 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -342,3 +342,8 @@ int ASensor_getHighestDirectReportRateLevel(ASensor const *sensor) { RETURN_IF_SENSOR_IS_NULL(ASENSOR_DIRECT_RATE_STOP); return static_cast<Sensor const *>(sensor)->getHighestDirectReportRateLevel(); } + +int ASensor_getHandle(ASensor const* sensor) { + RETURN_IF_SENSOR_IS_NULL(ASENSOR_INVALID); + return static_cast<Sensor const*>(sensor)->getHandle(); +} diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h index bb2ee9b5da04..0490e650a7a4 100644 --- a/native/webview/plat_support/draw_fn.h +++ b/native/webview/plat_support/draw_fn.h @@ -74,7 +74,10 @@ struct AwDrawFn_InitVkParams { VkQueue queue; uint32_t graphics_queue_index; uint32_t instance_version; - const char* const* enabled_extension_names; + const char* const* enabled_instance_extension_names; + uint32_t enabled_instance_extension_names_length; + const char* const* enabled_device_extension_names; + uint32_t enabled_device_extension_names_length; // Only one of device_features and device_features_2 should be non-null. // If both are null then no features are enabled. VkPhysicalDeviceFeatures* device_features; @@ -128,15 +131,13 @@ struct AwDrawFn_DrawVkParams { struct AwDrawFn_PostDrawVkParams { int version; - - // Input: Fence for the composite command buffer to signal it has finished its - // work on the GPU. - int fd; }; // Called on render thread while UI thread is blocked. Called for both GL and // VK. -typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params); +typedef void AwDrawFn_OnSync(int functor, + void* data, + AwDrawFn_OnSyncParams* params); // Called on render thread when either the context is destroyed _or_ when the // functor's last reference goes away. Will always be called with an active @@ -150,17 +151,24 @@ typedef void AwDrawFn_OnContextDestroyed(int functor, void* data); typedef void AwDrawFn_OnDestroyed(int functor, void* data); // Only called for GL. -typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params); +typedef void AwDrawFn_DrawGL(int functor, + void* data, + AwDrawFn_DrawGLParams* params); // Initialize vulkan state. Needs to be called again after any // OnContextDestroyed. Only called for Vulkan. -typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params); +typedef void AwDrawFn_InitVk(int functor, + void* data, + AwDrawFn_InitVkParams* params); // Only called for Vulkan. -typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params); +typedef void AwDrawFn_DrawVk(int functor, + void* data, + AwDrawFn_DrawVkParams* params); // Only called for Vulkan. -typedef void AwDrawFn_PostDrawVk(int functor, void* data, +typedef void AwDrawFn_PostDrawVk(int functor, + void* data, AwDrawFn_PostDrawVkParams* params); struct AwDrawFnFunctorCallbacks { @@ -183,7 +191,8 @@ enum AwDrawFnRenderMode { typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void); // Create a functor. |functor_callbacks| should be valid until OnDestroyed. -typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks); +typedef int AwDrawFn_CreateFunctor(void* data, + AwDrawFnFunctorCallbacks* functor_callbacks); // May be called on any thread to signal that the functor should be destroyed. // The functor will receive an onDestroyed when the last usage of it is diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp index 6c1ceaba4d46..b97bbc311624 100644 --- a/native/webview/plat_support/draw_functor.cpp +++ b/native/webview/plat_support/draw_functor.cpp @@ -74,6 +74,79 @@ void draw_gl(int functor, void* data, support->callbacks.draw_gl(functor, support->data, ¶ms); } +void initializeVk(int functor, void* data, + const uirenderer::VkFunctorInitParams& init_vk_params) { + SupportData* support = static_cast<SupportData*>(data); + VkPhysicalDeviceFeatures2 device_features_2; + if (init_vk_params.device_features_2) + device_features_2 = *init_vk_params.device_features_2; + + AwDrawFn_InitVkParams params{ + .version = kAwDrawFnVersion, + .instance = init_vk_params.instance, + .physical_device = init_vk_params.physical_device, + .device = init_vk_params.device, + .queue = init_vk_params.queue, + .graphics_queue_index = init_vk_params.graphics_queue_index, + .instance_version = init_vk_params.instance_version, + .enabled_instance_extension_names = + init_vk_params.enabled_instance_extension_names, + .enabled_instance_extension_names_length = + init_vk_params.enabled_instance_extension_names_length, + .enabled_device_extension_names = + init_vk_params.enabled_device_extension_names, + .enabled_device_extension_names_length = + init_vk_params.enabled_device_extension_names_length, + .device_features = nullptr, + .device_features_2 = + init_vk_params.device_features_2 ? &device_features_2 : nullptr, + }; + support->callbacks.init_vk(functor, support->data, ¶ms); +} + +void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) { + SupportData* support = static_cast<SupportData*>(data); + float gabcdef[7]; + draw_vk_params.color_space_ptr->transferFn(gabcdef); + AwDrawFn_DrawVkParams params{ + .version = kAwDrawFnVersion, + .width = draw_vk_params.width, + .height = draw_vk_params.height, + .is_layer = draw_vk_params.is_layer, + .secondary_command_buffer = draw_vk_params.secondary_command_buffer, + .color_attachment_index = draw_vk_params.color_attachment_index, + .compatible_render_pass = draw_vk_params.compatible_render_pass, + .format = draw_vk_params.format, + .transfer_function_g = gabcdef[0], + .transfer_function_a = gabcdef[1], + .transfer_function_b = gabcdef[2], + .transfer_function_c = gabcdef[3], + .transfer_function_d = gabcdef[4], + .transfer_function_e = gabcdef[5], + .transfer_function_f = gabcdef[6], + .clip_left = draw_vk_params.clip_left, + .clip_top = draw_vk_params.clip_top, + .clip_right = draw_vk_params.clip_right, + .clip_bottom = draw_vk_params.clip_bottom, + }; + COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3), + gamut_transform_size_mismatch); + draw_vk_params.color_space_ptr->toXYZD50( + reinterpret_cast<skcms_Matrix3x3*>(¶ms.color_space_toXYZD50)); + COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_vk_params.transform), + mismatched_transform_matrix_sizes); + for (int i = 0; i < NELEM(params.transform); ++i) { + params.transform[i] = draw_vk_params.transform[i]; + } + support->callbacks.draw_vk(functor, support->data, ¶ms); +} + +void postDrawVk(int functor, void* data) { + SupportData* support = static_cast<SupportData*>(data); + AwDrawFn_PostDrawVkParams params{.version = kAwDrawFnVersion}; + support->callbacks.post_draw_vk(functor, support->data, ¶ms); +} + int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { static bool callbacks_initialized = false; static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = { @@ -82,9 +155,19 @@ int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { .onDestroyed = &onDestroyed, }; if (!callbacks_initialized) { - // Under uirenderer::RenderMode::Vulkan, whether gles or vk union should - // be populated should match whether the vk-gl interop is used. - webview_functor_callbacks.gles.draw = &draw_gl; + switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) { + case uirenderer::RenderMode::OpenGL_ES: + webview_functor_callbacks.gles.draw = &draw_gl; + break; + case uirenderer::RenderMode::Vulkan: + webview_functor_callbacks.vk.initialize = &initializeVk; + webview_functor_callbacks.vk.draw = &drawVk; + webview_functor_callbacks.vk.postDraw = &postDrawVk; + // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor + // no longer uses GL interop. + webview_functor_callbacks.gles.draw = &draw_gl; + break; + } callbacks_initialized = true; } SupportData* support = new SupportData{ diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 03c620580423..16bfcc9e739f 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -99,8 +99,12 @@ <string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string> <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] --> <string name="connected_via_passpoint">Connected via %1$s</string> + <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] --> + <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string> <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] --> <string name="available_via_passpoint">Available via %1$s</string> + <!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] --> + <string name="tap_to_set_up">Tap to set up</string> <!-- Package name for Settings app--> <string name="settings_package" translatable="false">com.android.settings</string> <!-- Package name for Certinstaller app--> @@ -123,6 +127,79 @@ <!-- Status message of Wi-Fi when an available network is a carrier network. [CHAR LIMIT=NONE] --> <string name="available_via_carrier">Available via %1$s</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_AP_CONNECTION. [CHAR LIMIT=NONE] --> + <string name="osu_failure_ap_connection">Connection failed</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_URL_INVALID. [CHAR LIMIT=NONE] --> + <string name="osu_failure_server_url_invalid">Invalid OSU server URL</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_CONNECTION. [CHAR LIMIT=NONE] --> + <string name="osu_failure_server_connection">OSU server connection failed</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_VALIDATION. [CHAR LIMIT=NONE] --> + <string name="osu_failure_server_validation">OSU server validation failed</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION. [CHAR LIMIT=NONE] --> + <string name="osu_failure_service_provider_verification">Invalid OSU server certificate</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_ABORTED. [CHAR LIMIT=NONE] --> + <string name="osu_failure_provisioning_aborted">Provisioning aborted</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_NOT_AVAILABLE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_provisioning_not_available">Provisioning not available</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_INVALID_SERVER_URL. [CHAR LIMIT=NONE] --> + <string name="osu_failure_invalid_server_url">Invalid OSU server URL</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_COMMAND_TYPE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_unexpected_command_type">Unexpected command type</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_unexpected_soap_message_type">Unexpected SOAP message type</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_SOAP_MESSAGE_EXCHANGE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_soap_message_exchange">SOAP message exchange failed</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_START_REDIRECT_LISTENER. [CHAR LIMIT=NONE] --> + <string name="osu_failure_start_redirect_listener">Redirect listener failed to start</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER. [CHAR LIMIT=NONE] --> + <string name="osu_failure_timed_out_redirect_listener">Timed out waiting for redirect</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_OSU_ACTIVITY_FOUND. [CHAR LIMIT=NONE] --> + <string name="osu_failure_no_osu_activity_found">No OSU activity found</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS. [CHAR LIMIT=NONE] --> + <string name="osu_failure_unexpected_soap_message_status">Unexpected SOAP message status</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_PPS_MO. [CHAR LIMIT=NONE] --> + <string name="osu_failure_no_pps_mo">Failed to find PPS-MO</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_no_aaa_server_trust_root_node">Failed to find trust root node for AAA server</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_no_remediation_server_trust_root_node">Failed to find trust root node for remediation server</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_no_policy_server_trust_root_node">Failed to find trust root node for policy server</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES. [CHAR LIMIT=NONE] --> + <string name="osu_failure_retrieve_trust_root_certificates">Failed to retrieve trust root certificates</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE. [CHAR LIMIT=NONE] --> + <string name="osu_failure_no_aaa_trust_root_certificate">Failed to find trust root certificate for AAA server</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION. [CHAR LIMIT=NONE] --> + <string name="osu_failure_add_passpoint_configuration">Failed to add PassPoint configuration</string> + <!-- Status message of OSU Provider on receiving OSU_FAILURE_OSU_PROVIDER_NOT_FOUND. [CHAR LIMIT=NONE] --> + <string name="osu_failure_osu_provider_not_found">Failed to find an OSU provider</string> + + <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTING. [CHAR LIMIT=NONE] --> + <string name="osu_status_ap_connecting">Connecting</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTED. [CHAR LIMIT=NONE] --> + <string name="osu_status_ap_connected">Connected</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTING. [CHAR LIMIT=NONE] --> + <string name="osu_status_server_connecting">Connecting to OSU server</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_VALIDATED. [CHAR LIMIT=NONE] --> + <string name="osu_status_server_validated">OSU server validated</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTED. [CHAR LIMIT=NONE] --> + <string name="osu_status_server_connected">Connected to OSU server</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_INIT_SOAP_EXCHANGE. [CHAR LIMIT=NONE] --> + <string name="osu_status_init_soap_exchange">Initial SOAP exchange</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE. [CHAR LIMIT=NONE] --> + <string name="osu_status_waiting_for_redirect_response">Waiting for redirect response</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_REDIRECT_RESPONSE_RECEIVED. [CHAR LIMIT=NONE] --> + <string name="osu_status_redirect_response_received">Received redirect response</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_SECOND_SOAP_EXCHANGE. [CHAR LIMIT=NONE] --> + <string name="osu_status_second_soap_exchange">Second SOAP exchange</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_THIRD_SOAP_EXCHANGE. [CHAR LIMIT=NONE] --> + <string name="osu_status_third_soap_exchange">Third SOAP exchange</string> + <!-- Status message of OSU Provider on receiving OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS. [CHAR LIMIT=NONE] --> + <string name="osu_status_retrieving_trust_root_certs">Retrieving trust root certificates</string> + + <!-- Status message of OSU Provider on completing provisioning. [CHAR LIMIT=NONE] --> + <string name="osu_provisioning_complete">Provisioning complete</string> + <!-- Speed label for very slow network speed --> <string name="speed_label_very_slow">Very Slow</string> <!-- Speed label for slow network speed --> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 595aeb3ecc10..c751c39aebbd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -38,8 +38,8 @@ public class Utils { private static final String CURRENT_MODE_KEY = "CURRENT_MODE"; private static final String NEW_MODE_KEY = "NEW_MODE"; @VisibleForTesting - static final String STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY = - "ro.storage_manager.show_opt_in"; + static final String STORAGE_MANAGER_ENABLED_PROPERTY = + "ro.storage_manager.enabled"; private static Signature[] sSystemSignature; private static String sPermissionControllerPackageName; @@ -373,8 +373,7 @@ public class Utils { public static boolean isStorageManagerEnabled(Context context) { boolean isDefaultOn; try { - // Turn off by default if the opt-in was shown. - isDefaultOn = !SystemProperties.getBoolean(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, true); + isDefaultOn = SystemProperties.getBoolean(STORAGE_MANAGER_ENABLED_PROPERTY, false); } catch (Resources.NotFoundException e) { isDefaultOn = false; } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java index 12b8efb1461c..4ab9a9ac5915 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java @@ -55,6 +55,12 @@ public final class CategoryKey { "com.android.settings.category.ia.privacy"; public static final String CATEGORY_ENTERPRISE_PRIVACY = "com.android.settings.category.ia.enterprise_privacy"; + public static final String CATEGORY_ABOUT_LEGAL = + "com.android.settings.category.ia.about_legal"; + public static final String CATEGORY_MY_DEVICE_INFO = + "com.android.settings.category.ia.my_device_info"; + public static final String CATEGORY_BATTERY_SAVER_SETTINGS = + "com.android.settings.category.ia.battery_saver_settings"; public static final Map<String, String> KEY_COMPAT_MAP; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index af5a24f16222..27dc628ac094 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -41,7 +41,9 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.ProvisioningCallback; import android.os.Bundle; import android.os.Parcelable; import android.os.RemoteException; @@ -182,6 +184,10 @@ public class AccessPoint implements Comparable<AccessPoint> { public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE; + public static final String KEY_PREFIX_AP = "AP:"; + public static final String KEY_PREFIX_FQDN = "FQDN:"; + public static final String KEY_PREFIX_OSU = "OSU:"; + private final Context mContext; private String ssid; @@ -204,9 +210,6 @@ public class AccessPoint implements Comparable<AccessPoint> { @Speed private int mSpeed = Speed.NONE; private boolean mIsScoredNetworkMetered = false; - // used to co-relate internal vs returned accesspoint. - int mId; - /** * Information associated with the {@link PasspointConfiguration}. Only maintaining * the relevant info to preserve spaces. @@ -215,6 +218,13 @@ public class AccessPoint implements Comparable<AccessPoint> { private String mProviderFriendlyName; private boolean mIsCarrierAp = false; + + private OsuProvider mOsuProvider; + + private String mOsuStatus; + private String mOsuFailure; + private boolean mOsuProvisioningComplete = false; + /** * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP. */ @@ -280,14 +290,18 @@ public class AccessPoint implements Comparable<AccessPoint> { // Calculate required fields updateKey(); updateRssi(); - - mId = sLastId.incrementAndGet(); } + /** + * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks + * page. + * + * Passpoint Credential AccessPoints should be created with this. + * Make sure to call setScanResults after constructing with this. + */ public AccessPoint(Context context, WifiConfiguration config) { mContext = context; loadConfig(config); - mId = sLastId.incrementAndGet(); } /** @@ -298,7 +312,17 @@ public class AccessPoint implements Comparable<AccessPoint> { mContext = context; mFqdn = config.getHomeSp().getFqdn(); mProviderFriendlyName = config.getHomeSp().getFriendlyName(); - mId = sLastId.incrementAndGet(); + } + + /** + * Initialize an AccessPoint object for a Passpoint OSU Provider. + * Make sure to call setScanResults after constructing with this. + */ + public AccessPoint(Context context, OsuProvider provider) { + mContext = context; + mOsuProvider = provider; + ssid = provider.getFriendlyName(); + updateKey(); } AccessPoint(Context context, Collection<ScanResult> results) { @@ -324,8 +348,6 @@ public class AccessPoint implements Comparable<AccessPoint> { mIsCarrierAp = firstResult.isCarrierAp; mCarrierApEapType = firstResult.carrierApEapType; mCarrierName = firstResult.carrierName; - - mId = sLastId.incrementAndGet(); } @VisibleForTesting void loadConfig(WifiConfiguration config) { @@ -340,19 +362,13 @@ public class AccessPoint implements Comparable<AccessPoint> { /** Updates {@link #mKey} and should only called upon object creation/initialization. */ private void updateKey() { // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo - - StringBuilder builder = new StringBuilder(); - if (isPasspoint()) { - builder.append(mConfig.FQDN); - } else if (TextUtils.isEmpty(getSsidStr())) { - builder.append(getBssid()); - } else { - builder.append(getSsidStr()); + mKey = getKey(mConfig); + } else if (isOsuProvider()) { + mKey = getKey(mOsuProvider); + } else { // Non-Passpoint AP + mKey = getKey(getSsidStr(), getBssid(), getSecurity()); } - - builder.append(',').append(getSecurity()); - mKey = builder.toString(); } /** @@ -396,8 +412,8 @@ public class AccessPoint implements Comparable<AccessPoint> { return difference; } - // Sort by ssid. - difference = getSsidStr().compareToIgnoreCase(other.getSsidStr()); + // Sort by title. + difference = getTitle().compareToIgnoreCase(other.getTitle()); if (difference != 0) { return difference; } @@ -593,30 +609,46 @@ public class AccessPoint implements Comparable<AccessPoint> { } public static String getKey(ScanResult result) { - StringBuilder builder = new StringBuilder(); + return getKey(result.SSID, result.BSSID, getSecurity(result)); + } - if (TextUtils.isEmpty(result.SSID)) { - builder.append(result.BSSID); + /** + * Returns the AccessPoint key for a WifiConfiguration. + * This will return a special Passpoint key if the config is for Passpoint. + */ + public static String getKey(WifiConfiguration config) { + if (config.isPasspoint()) { + return new StringBuilder() + .append(KEY_PREFIX_FQDN) + .append(config.FQDN).toString(); } else { - builder.append(result.SSID); + return getKey(config.SSID, config.BSSID, getSecurity(config)); } + } - builder.append(',').append(getSecurity(result)); - return builder.toString(); + /** + * Returns the AccessPoint key corresponding to the OsuProvider. + */ + public static String getKey(OsuProvider provider) { + return new StringBuilder() + .append(KEY_PREFIX_OSU) + .append(provider.getFriendlyName()) + .append(',') + .append(provider.getServerUri()).toString(); } - public static String getKey(WifiConfiguration config) { + /** + * Returns the AccessPoint key for a normal non-Passpoint network by ssid/bssid and security. + */ + private static String getKey(String ssid, String bssid, int security) { StringBuilder builder = new StringBuilder(); - - if (config.isPasspoint()) { - builder.append(config.FQDN); - } else if (TextUtils.isEmpty(config.SSID)) { - builder.append(config.BSSID); + builder.append(KEY_PREFIX_AP); + if (TextUtils.isEmpty(ssid)) { + builder.append(bssid); } else { - builder.append(removeDoubleQuotes(config.SSID)); + builder.append(ssid); } - - builder.append(',').append(getSecurity(config)); + builder.append(',').append(security); return builder.toString(); } @@ -839,91 +871,105 @@ public class AccessPoint implements Comparable<AccessPoint> { public String getTitle() { if (isPasspoint()) { return mConfig.providerFriendlyName; + } else if (isOsuProvider()) { + return mOsuProvider.getFriendlyName(); } else { return getSsidStr(); } } public String getSummary() { - return getSettingsSummary(mConfig); + return getSettingsSummary(); } public String getSettingsSummary() { - return getSettingsSummary(mConfig); - } - - private String getSettingsSummary(WifiConfiguration config) { // Update to new summary StringBuilder summary = new StringBuilder(); - if (isActive() && config != null && config.isPasspoint()) { - // This is the active connection on passpoint - summary.append(getSummary(mContext, getDetailedState(), - false, config.providerFriendlyName)); - } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED - && mIsCarrierAp) { - summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName)); + if (isOsuProvider()) { + if (mOsuProvisioningComplete) { + summary.append(mContext.getString(R.string.osu_provisioning_complete)); + } else if (mOsuFailure != null) { + summary.append(mOsuFailure); + } else if (mOsuStatus != null) { + summary.append(mOsuStatus); + } else { + summary.append(mContext.getString(R.string.tap_to_set_up)); + } } else if (isActive()) { - // This is the active connection on non-passpoint network - summary.append(getSummary(mContext, getDetailedState(), - mInfo != null && mInfo.isEphemeral())); - } else if (config != null && config.isPasspoint() - && config.getNetworkSelectionStatus().isNetworkEnabled()) { - String format = mContext.getString(R.string.available_via_passpoint); - summary.append(String.format(format, config.providerFriendlyName)); - } else if (config != null && config.hasNoInternetAccess()) { - int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() - ? R.string.wifi_no_internet_no_reconnect - : R.string.wifi_no_internet; - summary.append(mContext.getString(messageID)); - } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) { - WifiConfiguration.NetworkSelectionStatus networkStatus = - config.getNetworkSelectionStatus(); - switch (networkStatus.getNetworkSelectionDisableReason()) { - case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: - summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: - summary.append(mContext.getString(R.string.wifi_check_password_try_again)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: - case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE: - summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: - summary.append(mContext.getString(R.string.wifi_disabled_generic)); - break; + if (isPasspoint()) { + // This is the active connection on passpoint + summary.append(getSummary(mContext, ssid, getDetailedState(), + false, mConfig.providerFriendlyName)); + } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED + && mIsCarrierAp) { + // This is the active connection on a carrier AP + summary.append(String.format(mContext.getString(R.string.connected_via_carrier), + mCarrierName)); + } else { + // This is the active connection on non-passpoint network + summary.append(getSummary(mContext, getDetailedState(), + mInfo != null && mInfo.isEphemeral())); } - } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) { - summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider)); - } else if (mIsCarrierAp) { - summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName)); - } else if (!isReachable()) { // Wifi out of range - summary.append(mContext.getString(R.string.wifi_not_in_range)); - } else { // In range, not disabled. - if (config != null) { // Is saved network - // Last attempt to connect to this failed. Show reason why - switch (config.recentFailure.getAssociationStatus()) { - case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: - summary.append(mContext.getString( - R.string.wifi_ap_unable_to_handle_new_sta)); + } else { // not active + if (mConfig != null && mConfig.hasNoInternetAccess()) { + int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() + ? R.string.wifi_no_internet_no_reconnect + : R.string.wifi_no_internet; + summary.append(mContext.getString(messageID)); + } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) { + WifiConfiguration.NetworkSelectionStatus networkStatus = + mConfig.getNetworkSelectionStatus(); + switch (networkStatus.getNetworkSelectionDisableReason()) { + case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: + summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); break; - default: - // "Saved" - summary.append(mContext.getString(R.string.wifi_remembered)); + case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: + summary.append(mContext.getString(R.string.wifi_check_password_try_again)); + break; + case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: + case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE: + summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); break; + case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: + summary.append(mContext.getString(R.string.wifi_disabled_generic)); + break; + } + } else if (mConfig != null && mConfig.getNetworkSelectionStatus().isNotRecommended()) { + summary.append(mContext.getString( + R.string.wifi_disabled_by_recommendation_provider)); + } else if (mIsCarrierAp) { + summary.append(String.format(mContext.getString( + R.string.available_via_carrier), mCarrierName)); + } else if (!isReachable()) { // Wifi out of range + summary.append(mContext.getString(R.string.wifi_not_in_range)); + } else { // In range, not disabled. + if (mConfig != null) { // Is saved network + // Last attempt to connect to this failed. Show reason why + switch (mConfig.recentFailure.getAssociationStatus()) { + case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: + summary.append(mContext.getString( + R.string.wifi_ap_unable_to_handle_new_sta)); + break; + default: + // "Saved" + summary.append(mContext.getString(R.string.wifi_remembered)); + break; + } } } } + + if (isVerboseLoggingEnabled()) { - summary.append(WifiUtils.buildLoggingSummary(this, config)); + summary.append(WifiUtils.buildLoggingSummary(this, mConfig)); } - if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) { + if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) { return mContext.getResources().getString( R.string.preference_summary_default_combination, - WifiUtils.getMeteredLabel(mContext, config), + WifiUtils.getMeteredLabel(mContext, mConfig), summary.toString()); } @@ -976,11 +1022,33 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** + * Return true if this AccessPoint represents an OSU Provider. + */ + public boolean isOsuProvider() { + return mOsuProvider != null; + } + + /** + * Starts the OSU Provisioning flow. + */ + public void startOsuProvisioning() { + mContext.getSystemService(WifiManager.class).startSubscriptionProvisioning( + mOsuProvider, + new AccessPointProvisioningCallback(), + ThreadUtils.getUiThreadHandler() + ); + } + + /** * Return whether the given {@link WifiInfo} is for this access point. * If the current AP does not have a network Id then the config is used to * match based on SSID and security. */ private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) { + if (info.isOsuAp()) { + return (mOsuStatus != null); + } + if (isPasspoint() == false && networkId != WifiConfiguration.INVALID_NETWORK_ID) { return networkId == info.getNetworkId(); } else if (config != null) { @@ -1065,8 +1133,8 @@ public class AccessPoint implements Comparable<AccessPoint> { void setScanResults(Collection<ScanResult> scanResults) { // Validate scan results are for current AP only by matching SSID/BSSID - // Passpoint R1 networks are not bound to a specific SSID/BSSID, so skip this for passpoint. - if (!isPasspoint()) { + // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint. + if (!isPasspoint() && !isOsuProvider()) { String key = getKey(); for (ScanResult result : scanResults) { String scanResultKey = AccessPoint.getKey(result); @@ -1119,7 +1187,17 @@ public class AccessPoint implements Comparable<AccessPoint> { } } - /** Attempt to update the AccessPoint and return true if an update occurred. */ + /** + * Attempt to update the AccessPoint with the current connection info. + * This is used to set an AccessPoint to the active one if the connection info matches, or + * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI + * is also updated upon a match. Listeners will be notified if an update occurred. + * + * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling + * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker. + * + * Returns true if an update occurred. + */ public boolean update( @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) { @@ -1246,11 +1324,11 @@ public class AccessPoint implements Comparable<AccessPoint> { public static String getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String passpointProvider) { - if (state == DetailedState.CONNECTED && ssid == null) { - if (TextUtils.isEmpty(passpointProvider) == false) { + if (state == DetailedState.CONNECTED) { + if (!TextUtils.isEmpty(passpointProvider)) { // Special case for connected + passpoint networks. - String format = context.getString(R.string.connected_via_passpoint); - return String.format(format, passpointProvider); + String format = context.getString(R.string.ssid_by_passpoint_provider); + return String.format(format, ssid, passpointProvider); } else if (isEphemeral) { // Special case for connected + ephemeral networks. final NetworkScoreManager networkScoreManager = context.getSystemService( @@ -1443,4 +1521,166 @@ public class AccessPoint implements Comparable<AccessPoint> { private static boolean isVerboseLoggingEnabled() { return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE); } + + /** + * Callbacks relaying changes to the OSU provisioning status started in startOsuProvisioning(). + * + * All methods are invoked on the Main Thread + */ + private class AccessPointProvisioningCallback extends ProvisioningCallback { + // TODO: Remove logs and implement summary changing logic for these provisioning callbacks. + @Override + @MainThread public void onProvisioningFailure(int status) { + switch (status) { + case OSU_FAILURE_AP_CONNECTION: + mOsuFailure = mContext.getString(R.string.osu_failure_ap_connection); + break; + case OSU_FAILURE_SERVER_URL_INVALID: + mOsuFailure = mContext.getString(R.string.osu_failure_server_url_invalid); + break; + case OSU_FAILURE_SERVER_CONNECTION: + mOsuFailure = mContext.getString(R.string.osu_failure_server_connection); + break; + case OSU_FAILURE_SERVER_VALIDATION: + mOsuFailure = mContext.getString(R.string.osu_failure_server_validation); + break; + case OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION: + mOsuFailure = mContext.getString( + R.string.osu_failure_service_provider_verification); + break; + case OSU_FAILURE_PROVISIONING_ABORTED: + mOsuFailure = mContext.getString(R.string.osu_failure_provisioning_aborted); + break; + case OSU_FAILURE_PROVISIONING_NOT_AVAILABLE: + mOsuFailure = mContext.getString( + R.string.osu_failure_provisioning_not_available); + break; + case OSU_FAILURE_INVALID_SERVER_URL: + mOsuFailure = mContext.getString(R.string.osu_failure_invalid_server_url); + break; + case OSU_FAILURE_UNEXPECTED_COMMAND_TYPE: + mOsuFailure = mContext.getString(R.string.osu_failure_unexpected_command_type); + break; + case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE: + mOsuFailure = mContext.getString( + R.string.osu_failure_unexpected_soap_message_type); + break; + case OSU_FAILURE_SOAP_MESSAGE_EXCHANGE: + mOsuFailure = mContext.getString(R.string.osu_failure_soap_message_exchange); + break; + case OSU_FAILURE_START_REDIRECT_LISTENER: + mOsuFailure = mContext.getString(R.string.osu_failure_start_redirect_listener); + break; + case OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER: + mOsuFailure = mContext.getString( + R.string.osu_failure_timed_out_redirect_listener); + break; + case OSU_FAILURE_NO_OSU_ACTIVITY_FOUND: + mOsuFailure = mContext.getString(R.string.osu_failure_no_osu_activity_found); + break; + case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS: + mOsuFailure = mContext.getString( + R.string.osu_failure_unexpected_soap_message_status); + break; + case OSU_FAILURE_NO_PPS_MO: + mOsuFailure = mContext.getString( + R.string.osu_failure_no_pps_mo); + break; + case OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE: + mOsuFailure = mContext.getString( + R.string.osu_failure_no_aaa_server_trust_root_node); + break; + case OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE: + mOsuFailure = mContext.getString( + R.string.osu_failure_no_remediation_server_trust_root_node); + break; + case OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE: + mOsuFailure = mContext.getString( + R.string.osu_failure_no_policy_server_trust_root_node); + break; + case OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES: + mOsuFailure = mContext.getString( + R.string.osu_failure_retrieve_trust_root_certificates); + break; + case OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE: + mOsuFailure = mContext.getString( + R.string.osu_failure_no_aaa_trust_root_certificate); + break; + case OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION: + mOsuFailure = mContext.getString( + R.string.osu_failure_add_passpoint_configuration); + break; + case OSU_FAILURE_OSU_PROVIDER_NOT_FOUND: + mOsuFailure = mContext.getString(R.string.osu_failure_osu_provider_not_found); + break; + } + mOsuStatus = null; + mOsuProvisioningComplete = false; + ThreadUtils.postOnMainThread(() -> { + if (mAccessPointListener != null) { + mAccessPointListener.onAccessPointChanged(AccessPoint.this); + } + }); + } + + @Override + @MainThread public void onProvisioningStatus(int status) { + switch (status) { + case OSU_STATUS_AP_CONNECTING: + mOsuStatus = mContext.getString(R.string.osu_status_ap_connecting); + break; + case OSU_STATUS_AP_CONNECTED: + mOsuStatus = mContext.getString(R.string.osu_status_ap_connected); + break; + case OSU_STATUS_SERVER_CONNECTING: + mOsuStatus = mContext.getString(R.string.osu_status_server_connecting); + break; + case OSU_STATUS_SERVER_VALIDATED: + mOsuStatus = mContext.getString(R.string.osu_status_server_validated); + break; + case OSU_STATUS_SERVER_CONNECTED: + mOsuStatus = mContext.getString(R.string.osu_status_server_connected); + break; + case OSU_STATUS_INIT_SOAP_EXCHANGE: + mOsuStatus = mContext.getString(R.string.osu_status_init_soap_exchange); + break; + case OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE: + mOsuStatus = mContext.getString( + R.string.osu_status_waiting_for_redirect_response); + break; + case OSU_STATUS_REDIRECT_RESPONSE_RECEIVED: + mOsuStatus = mContext.getString(R.string.osu_status_redirect_response_received); + break; + case OSU_STATUS_SECOND_SOAP_EXCHANGE: + mOsuStatus = mContext.getString(R.string.osu_status_second_soap_exchange); + break; + case OSU_STATUS_THIRD_SOAP_EXCHANGE: + mOsuStatus = mContext.getString(R.string.osu_status_third_soap_exchange); + break; + case OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS: + mOsuStatus = mContext.getString( + R.string.osu_status_retrieving_trust_root_certs); + break; + } + mOsuFailure = null; + mOsuProvisioningComplete = false; + ThreadUtils.postOnMainThread(() -> { + if (mAccessPointListener != null) { + mAccessPointListener.onAccessPointChanged(AccessPoint.this); + } + }); + } + + @Override + @MainThread public void onProvisioningComplete() { + mOsuProvisioningComplete = true; + mOsuFailure = null; + mOsuStatus = null; + ThreadUtils.postOnMainThread(() -> { + if (mAccessPointListener != null) { + mAccessPointListener.onAccessPointChanged(AccessPoint.this); + } + }); + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 79a72402e232..b9a5f2347f16 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -35,6 +35,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiNetworkScoreCache.CacheListener; +import android.net.wifi.hotspot2.OsuProvider; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -67,6 +68,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -584,7 +586,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) { WifiConfiguration config = pairing.first; - // TODO: Prioritize home networks before roaming networks List<ScanResult> scanResults = new ArrayList<>(); List<ScanResult> homeScans = @@ -599,8 +600,12 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro roamingScans = new ArrayList<>(); } - scanResults.addAll(homeScans); - scanResults.addAll(roamingScans); + // TODO(b/118705403): Differentiate home network vs roaming network for summary info + if (!homeScans.isEmpty()) { + scanResults.addAll(homeScans); + } else { + scanResults.addAll(roamingScans); + } if (seenFQDNs.add(config.FQDN)) { int bestRssi = Integer.MIN_VALUE; @@ -611,13 +616,30 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } - AccessPoint accessPoint = new AccessPoint(mContext, config); - accessPoint.setScanResults(scanResults); + AccessPoint accessPoint = + getCachedOrCreatePasspoint(scanResults, cachedAccessPoints, config); accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); accessPoints.add(accessPoint); } } + // Add Passpoint OSU Provider AccessPoints + Map<OsuProvider, List<ScanResult>> providersAndScans = + mWifiManager.getMatchingOsuProviders(cachedScanResults); + Set<OsuProvider> alreadyProvisioned = mWifiManager + .getMatchingPasspointConfigsForOsuProviders( + providersAndScans.keySet()).keySet(); + for (OsuProvider provider : providersAndScans.keySet()) { + if (!alreadyProvisioned.contains(provider)) { + AccessPoint accessPointOsu = + getCachedOrCreateOsu(providersAndScans.get(provider), + cachedAccessPoints, provider); + accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo); + accessPoints.add(accessPointOsu); + } + } + + // If there were no scan results, create an AP for the currently connected network (if // it exists). if (accessPoints.isEmpty() && connectionConfig != null) { @@ -667,18 +689,51 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro AccessPoint getCachedOrCreate( List<ScanResult> scanResults, List<AccessPoint> cache) { - final int N = cache.size(); - for (int i = 0; i < N; i++) { - if (cache.get(i).getKey().equals(AccessPoint.getKey(scanResults.get(0)))) { - AccessPoint ret = cache.remove(i); - ret.setScanResults(scanResults); - return ret; - } + AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0))); + if (accessPoint == null) { + accessPoint = new AccessPoint(mContext, scanResults); + } else { + accessPoint.setScanResults(scanResults); + } + return accessPoint; + } + + private AccessPoint getCachedOrCreatePasspoint( + List<ScanResult> scanResults, + List<AccessPoint> cache, + WifiConfiguration config) { + AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(config)); + if (accessPoint == null) { + accessPoint = new AccessPoint(mContext, config); + } + accessPoint.setScanResults(scanResults); + return accessPoint; + } + + private AccessPoint getCachedOrCreateOsu( + List<ScanResult> scanResults, + List<AccessPoint> cache, + OsuProvider provider) { + AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(provider)); + if (accessPoint == null) { + accessPoint = new AccessPoint(mContext, provider); } - final AccessPoint accessPoint = new AccessPoint(mContext, scanResults); + accessPoint.setScanResults(scanResults); return accessPoint; } + private AccessPoint getCachedByKey(List<AccessPoint> cache, String key) { + ListIterator<AccessPoint> lit = cache.listIterator(); + while (lit.hasNext()) { + AccessPoint currentAccessPoint = lit.next(); + if (currentAccessPoint.getKey().equals(key)) { + lit.remove(); + return currentAccessPoint; + } + } + return null; + } + private void updateNetworkInfo(NetworkInfo networkInfo) { /* Sticky broadcasts can call this when wifi is disabled */ diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 86f04389604c..92ebe446ee98 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -17,7 +17,7 @@ package com.android.settingslib; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; -import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY; +import static com.android.settingslib.Utils.STORAGE_MANAGER_ENABLED_PROPERTY; import static com.google.common.truth.Truth.assertThat; @@ -159,7 +159,7 @@ public class UtilsTest { @Test public void testIsStorageManagerEnabled_UsesSystemProperties() { - SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false"); + SystemProperties.set(STORAGE_MANAGER_ENABLED_PROPERTY, "true"); assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue(); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index bcf37ffef262..e843eb43a3a6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -705,14 +705,17 @@ class SettingsProtoDumpUtil { Settings.Global.GPU_DEBUG_LAYERS_GLES, GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES); dumpSetting(s, p, + Settings.Global.GUP_DEV_ALL_APPS, + GlobalSettingsProto.Gpu.GUP_DEV_ALL_APPS); + dumpSetting(s, p, Settings.Global.GUP_DEV_OPT_IN_APPS, GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS); dumpSetting(s, p, Settings.Global.GUP_DEV_OPT_OUT_APPS, GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS); dumpSetting(s, p, - Settings.Global.GUP_BLACK_LIST, - GlobalSettingsProto.Gpu.GUP_BLACK_LIST); + Settings.Global.GUP_BLACKLIST, + GlobalSettingsProto.Gpu.GUP_BLACKLIST); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); @@ -1879,6 +1882,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, SecureSettingsProto.Doze.PULSE_ON_DOUBLE_TAP); + dumpSetting(s, p, + Settings.Secure.DOZE_TAP_SCREEN_GESTURE, + SecureSettingsProto.Doze.PULSE_ON_TAP); p.end(dozeToken); dumpSetting(s, p, @@ -2363,6 +2369,14 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED); p.end(zenToken); + dumpSetting(s, p, + Settings.Secure.SKIP_GESTURE, + SecureSettingsProto.SKIP_GESTURE_ENABLED); + + dumpSetting(s, p, + Settings.Secure.SILENCE_GESTURE, + SecureSettingsProto.SILENCE_GESTURE_ENABLED); + // Please insert new settings using the same order as in SecureSettingsProto. p.end(token); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index bce559396ddd..5153f9ea86d1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1052,7 +1052,7 @@ public class SettingsProvider extends ContentProvider { } // TODO(b/117663715): Ensure the caller can access the setting. - // enforceSettingReadable(name, SETTINGS_TYPE_CONFIG, UserHandle.getCallingUserId()); + // enforceReadPermission(READ_DEVICE_CONFIG); // Get the value. synchronized (mLock) { @@ -1088,8 +1088,9 @@ public class SettingsProvider extends ContentProvider { private boolean mutateConfigSetting(String name, String value, String prefix, boolean makeDefault, int operation, int mode) { - // TODO(b/117663715): check the new permission when it's added. - // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); + + // TODO(b/117663715): Ensure the caller can access the setting. + // enforceReadPermission(WRITE_DEVICE_CONFIG); // Perform the mutation. synchronized (mLock) { diff --git a/packages/SettingsProvider/test/AndroidManifest.xml b/packages/SettingsProvider/test/AndroidManifest.xml index 87a4f603f70b..ebdf9b1a2791 100644 --- a/packages/SettingsProvider/test/AndroidManifest.xml +++ b/packages/SettingsProvider/test/AndroidManifest.xml @@ -20,6 +20,8 @@ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.WRITE_SETTINGS"/> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <uses-permission android:name="android.permission.MANAGE_USERS"/> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 9b775e058ea8..b903142c44c6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -57,6 +57,8 @@ <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" /> <!-- Development tool permissions granted to the shell. --> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index afb978174784..2d7471d9aca5 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -1953,8 +1953,7 @@ public class BugreportProgressService extends Service { @Override public void onProgress(int progress) throws RemoteException { - // TODO(b/111441001): change max argument? - updateProgressInfo(progress, CAPPED_MAX); + updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */); } @Override diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml new file mode 100644 index 000000000000..d3c3a518f6ac --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="200dp" + android:width="200dp" + android:viewportHeight="100" + android:viewportWidth="100"> + <path + android:fillColor="#000000" + android:pathData="M50.082,14.199m-13.985,0a13.985,13.985 0,1 1,27.97 0a13.985,13.985 0,1 1,-27.97 0"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml new file mode 100644 index 000000000000..a4417fb65da4 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="200dp" + android:width="200dp" + android:viewportHeight="100" + android:viewportWidth="100" > + <path + android:fillColor="#000000" + android:pathData="M50.082,0.025L50.082,0.025A13.985,15.63 0,0 1,64.067 15.656L64.067,49.029A13.985,15.63 0,0 1,50.082 64.659L50.082,64.659A13.985,15.63 0,0 1,36.097 49.029L36.097,15.656A13.985,15.63 0,0 1,50.082 0.025z"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml new file mode 100644 index 000000000000..6f7f39810608 --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<com.android.keyguard.clock.ClockLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <include + android:id="@+id/digital_clock" + layout="@layout/text_clock" + /> + <com.android.keyguard.clock.ImageClock + android:id="@+id/analog_clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + > + <ImageView + android:id="@+id/minute_hand" + android:layout_width="300dp" + android:layout_height="300dp" + android:src="@drawable/bubble_minute_hand" + android:tint="@color/bubbleMinuteHandColor" + /> + <ImageView + android:id="@+id/hour_hand" + android:layout_width="300dp" + android:layout_height="300dp" + android:src="@drawable/bubble_hour_hand" + android:tint="@color/bubbleHourHandColor" + /> + </com.android.keyguard.clock.ImageClock> +</com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml new file mode 100644 index 000000000000..e88e2c94e74f --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_alignParentTop="true"> + <include + android:id="@+id/lock_screen_clock" + layout="@layout/text_clock" + /> +</FrameLayout> + diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index d52866fbd444..463367b2c600 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -30,17 +30,9 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_alignParentTop="true"> - <TextClock + <include android:id="@+id/default_clock_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:letterSpacing="0.03" - android:textColor="?attr/wallpaperTextColor" - android:singleLine="true" - style="@style/widget_big" - android:format12Hour="@string/keyguard_widget_12_hours_format" - android:format24Hour="@string/keyguard_widget_24_hours_format" /> + layout="@layout/text_clock" /> </FrameLayout> <include layout="@layout/keyguard_status_area" android:id="@+id/keyguard_status_area" diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml new file mode 100644 index 000000000000..64b676f55fd6 --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<com.android.keyguard.clock.ClockLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <include + android:id="@+id/digital_clock" + layout="@layout/text_clock" + /> + <com.android.keyguard.clock.StretchAnalogClock + android:id="@+id/analog_clock" + android:layout_width="match_parent" + android:layout_height="match_parent" + /> +</com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml new file mode 100644 index 000000000000..b61ad9c4fc11 --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<TextClock + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:letterSpacing="0.03" + android:textColor="?attr/wallpaperTextColor" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + android:elegantTextHeight="false" +/> diff --git a/packages/SystemUI/res-keyguard/layout/type_clock.xml b/packages/SystemUI/res-keyguard/layout/type_clock.xml new file mode 100644 index 000000000000..21c64e9c7dbe --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/type_clock.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<com.android.keyguard.clock.ClockLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <com.android.keyguard.clock.TypographicClock + android:id="@+id/type_clock" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + <TextView + android:id="@+id/header" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="50dp" + style="@style/widget_big" + android:textColor="@color/typeClockAccentColor" + android:text="@string/type_clock_header" + android:textSize="40dp" + /> + <TextView + android:id="@+id/hour" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="50dp" + style="@style/widget_big" + android:textSize="40dp" + /> + <TextView + android:id="@+id/minute" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="50dp" + style="@style/widget_big" + android:textSize="40dp" + /> + </com.android.keyguard.clock.TypographicClock> +</com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/values/colors.xml b/packages/SystemUI/res-keyguard/values/colors.xml new file mode 100644 index 000000000000..74ee7ffad3f6 --- /dev/null +++ b/packages/SystemUI/res-keyguard/values/colors.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <!-- Default color for hour hand of Bubble clock. --> + <color name="bubbleHourHandColor">#C97343</color> + <!-- Default color for minute hand of Bubble clock. --> + <color name="bubbleMinuteHandColor">#F5C983</color> + <!-- Accent color for Typographic clock. --> + <color name="typeClockAccentColor">#F5C983</color> +</resources> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 1d5aa6d76991..7432f9cde639 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -402,4 +402,87 @@ number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item> </plurals> + <!-- Header for typographic clock face. [CHAR LIMIT=8] --> + <string name="type_clock_header">It\u2019s</string> + + <!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=8] --> + <string-array name="type_clock_hours"> + <item>Twelve</item> + <item>One</item> + <item>Two</item> + <item>Three</item> + <item>Four</item> + <item>Five</item> + <item>Six</item> + <item>Seven</item> + <item>Eight</item> + <item>Nine</item> + <item>Ten</item> + <item>Eleven</item> + </string-array> + + <!-- Minutes displayed in words on the typographic clock face. [CHAR LIMIT=16] --> + <string-array name="type_clock_minutes"> + <item>O\u2019Clock</item> + <item>O\u2019One</item> + <item>O\u2019Two</item> + <item>O\u2019Three</item> + <item>O\u2019Four</item> + <item>O\u2019Five</item> + <item>O\u2019Six</item> + <item>O\u2019Seven</item> + <item>O\u2019Eight</item> + <item>O\u2019Nine</item> + <item>Ten</item> + <item>Eleven</item> + <item>Twelve</item> + <item>Thirteen</item> + <item>Fourteen</item> + <item>Fifteen</item> + <item>Sixteen</item> + <item>Seventeen</item> + <item>Eighteen</item> + <item>Nineteen</item> + <item>Twenty</item> + <item>Twenty\nOne</item> + <item>Twenty\nTwo</item> + <item>Twenty\nThree</item> + <item>Twenty\nFour</item> + <item>Twenty\nFive</item> + <item>Twenty\nSix</item> + <item>Twenty\nSeven</item> + <item>Twenty\nEight</item> + <item>Twenty\nNine</item> + <item>Thirty</item> + <item>Thirty\nOne</item> + <item>Thirty\nTwo</item> + <item>Thirty\nThree</item> + <item>Thirty\nFour</item> + <item>Thirty\nFive</item> + <item>Thirty\nSix</item> + <item>Thirty\nSeven</item> + <item>Thirty\nEight</item> + <item>Thirty\nNine</item> + <item>Forty</item> + <item>Forty\nOne</item> + <item>Forty\nTwo</item> + <item>Forty\nThree</item> + <item>Forty\nFour</item> + <item>Forty\nFive</item> + <item>Forty\nSix</item> + <item>Forty\nSeven</item> + <item>Forty\nEight</item> + <item>Forty\nNine</item> + <item>Fifty</item> + <item>Fifty\nOne</item> + <item>Fifty\nTwo</item> + <item>Fifty\nThree</item> + <item>Fifty\nFour</item> + <item>Fifty\nFive</item> + <item>Fifty\nSix</item> + <item>Fifty\nSeven</item> + <item>Fifty\nEight</item> + <item>Fifty\nNine</item> + </string-array> + </resources> diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml index 7f4e0d21078f..f932303473bd 100644 --- a/packages/SystemUI/res/layout/global_actions_wrapped.xml +++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <com.android.systemui.HardwareUiLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top|right" diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 476089aa3aac..98f0cbe29110 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -106,7 +106,7 @@ <!-- The default tiles to display in QuickSettings --> <string name="quick_settings_tiles_default" translatable="false"> - wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast + wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,sensorprivacy </string> <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 633f8686b804..bd34beac7fd6 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -114,5 +114,8 @@ <item type="id" name="aod_mask_transition_progress_tag" /> <item type="id" name="aod_mask_transition_progress_end_tag" /> <item type="id" name="aod_mask_transition_progress_start_tag" /> + + <!-- Global Actions Menu --> + <item type="id" name="global_actions_view" /> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 523720d54eec..1a684a0ffb8f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -323,7 +323,7 @@ public class PluginInstanceManager<T extends Plugin> { return null; } // Create our own ClassLoader so we can use our own code as the parent. - ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName); + ClassLoader classLoader = mManager.getClassLoader(info); Context pluginContext = new PluginContextWrapper( mContext.createApplicationContext(info, 0), classLoader); Class<?> pluginClass = Class.forName(cls, true, classLoader); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index da143f9bcbe3..7139708b65b9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -14,6 +14,7 @@ package com.android.systemui.shared.plugins; +import android.app.LoadedApk; import android.app.Notification; import android.app.Notification.Action; import android.app.NotificationManager; @@ -44,15 +45,17 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper; import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; import dalvik.system.PathClassLoader; +import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; /** * @see Plugin @@ -117,6 +120,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage return mPluginEnabler; } + // TODO(mankoff): This appears to be only called from tests. Remove? public <T extends Plugin> T getOneShotPlugin(Class<T> cls) { ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class); if (info == null) { @@ -282,17 +286,25 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } } - public ClassLoader getClassLoader(String sourceDir, String pkg) { - if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) { - Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir + - ", pkg: " + pkg); + /** Returns class loader specific for the given plugin. */ + public ClassLoader getClassLoader(ApplicationInfo appInfo) { + if (!isDebuggable && !mWhitelistedPlugins.contains(appInfo.packageName)) { + Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + + appInfo.sourceDir + ", pkg: " + appInfo.packageName); return null; } - if (mClassLoaders.containsKey(pkg)) { - return mClassLoaders.get(pkg); + if (mClassLoaders.containsKey(appInfo.packageName)) { + return mClassLoaders.get(appInfo.packageName); } - ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader()); - mClassLoaders.put(pkg, classLoader); + + List<String> zipPaths = new ArrayList<>(); + List<String> libPaths = new ArrayList<>(); + LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths); + ClassLoader classLoader = new PathClassLoader( + TextUtils.join(File.pathSeparator, zipPaths), + TextUtils.join(File.pathSeparator, libPaths), + getParentClassLoader()); + mClassLoaders.put(appInfo.packageName, classLoader); return classLoader; } @@ -309,11 +321,6 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage return mParentClassLoader; } - public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException { - ClassLoader classLoader = getClassLoader(info.sourceDir, pkg); - return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader); - } - public <T> boolean dependsOn(Plugin p, Class<T> cls) { for (int i = 0; i < mPluginMap.size(); i++) { if (mPluginMap.valueAt(i).dependsOn(p, cls)) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 41e9ebaac4f6..a055950a5522 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -46,6 +46,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout protected View mEcaView; protected boolean mEnableHaptics; private boolean mDismissing; + protected boolean mResumed; private CountDownTimer mCountdownTimer = null; // To avoid accidental lockout due to events while the device in in the pocket, ignore @@ -263,6 +264,8 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout @Override public void onPause() { + mResumed = false; + if (mCountdownTimer != null) { mCountdownTimer.cancel(); mCountdownTimer = null; @@ -276,6 +279,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout @Override public void onResume(int reason) { + mResumed = true; } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 3cfd6a9ba7cb..1aff3949a74b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,9 +1,15 @@ package com.android.keyguard; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.graphics.Paint; import android.graphics.Paint.Style; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -12,6 +18,9 @@ import android.widget.TextClock; import androidx.annotation.VisibleForTesting; +import com.android.keyguard.clock.BubbleClockController; +import com.android.keyguard.clock.StretchAnalogClockController; +import com.android.keyguard.clock.TypeClockController; import com.android.systemui.Dependency; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.StatusBarState; @@ -19,13 +28,19 @@ import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; +import java.util.Objects; import java.util.TimeZone; import java.util.function.Consumer; +import java.util.function.Supplier; /** * Switch to show plugin clock when plugin is connected, otherwise it will show default clock. */ public class KeyguardClockSwitch extends RelativeLayout { + + private LayoutInflater mLayoutInflater; + + private final ContentResolver mContentResolver; /** * Optional/alternative clock injected via plugin. */ @@ -79,12 +94,25 @@ public class KeyguardClockSwitch extends RelativeLayout { } }; + private final ContentObserver mContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + if (mClockExtension != null) { + mClockExtension.reload(); + } + } + }; + public KeyguardClockSwitch(Context context) { this(context, null); } public KeyguardClockSwitch(Context context, AttributeSet attrs) { super(context, attrs); + mLayoutInflater = LayoutInflater.from(context); + mContentResolver = context.getContentResolver(); } /** @@ -108,7 +136,34 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockExtension = Dependency.get(ExtensionController.class).newExtension(ClockPlugin.class) .withPlugin(ClockPlugin.class) .withCallback(mClockPluginConsumer) + // Using withDefault even though this isn't the default as a workaround. + // ExtensionBulider doesn't provide the ability to supply a ClockPlugin + // instance based off of the value of a setting. Since multiple "default" + // can be provided, using a supplier that changes the settings value. + // A null return will cause Extension#reload to look at the next "default" + // supplier. + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + BubbleClockController.class.getName(), + () -> BubbleClockController.build(mLayoutInflater))) + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + StretchAnalogClockController.class.getName(), + () -> StretchAnalogClockController.build(mLayoutInflater))) + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + TypeClockController.class.getName(), + () -> TypeClockController.build(mLayoutInflater))) .build(); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), + false, mContentObserver); Dependency.get(StatusBarStateController.class).addCallback(mStateListener); } @@ -116,6 +171,7 @@ public class KeyguardClockSwitch extends RelativeLayout { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mClockExtension.destroy(); + mContentResolver.unregisterContentObserver(mContentObserver); Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); } @@ -199,10 +255,6 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockView.setShowCurrentUserTime(showCurrentUserTime); } - public void setElegantTextHeight(boolean elegant) { - mClockView.setElegantTextHeight(elegant); - } - public void setTextSize(int unit, float size) { mClockView.setTextSize(unit, size); } @@ -269,4 +321,44 @@ public class KeyguardClockSwitch extends RelativeLayout { StatusBarStateController.StateListener getStateListener() { return mStateListener; } + + /** + * Supplier that only gets an instance when a settings value matches expected value. + */ + private static class SettingsGattedSupplier implements Supplier<ClockPlugin> { + + private final ContentResolver mContentResolver; + private final String mKey; + private final String mValue; + private final Supplier<ClockPlugin> mSupplier; + + /** + * Constructs a supplier that changes secure setting key against value. + * + * @param contentResolver Used to look up settings value. + * @param key Settings key. + * @param value If the setting matches this values that get supplies a ClockPlugin + * instance. + * @param supplier Supplier of ClockPlugin instance, only used if the setting + * matches value. + */ + SettingsGattedSupplier(ContentResolver contentResolver, String key, String value, + Supplier<ClockPlugin> supplier) { + mContentResolver = contentResolver; + mKey = key; + mValue = value; + mSupplier = supplier; + } + + /** + * Returns null if the settings value doesn't match the expected value. + * + * A null return causes Extension#reload to skip this supplier and move to the next. + */ + @Override + public ClockPlugin get() { + final String currentValue = Settings.Secure.getString(mContentResolver, mKey); + return Objects.equals(currentValue, mValue) ? mSupplier.get() : null; + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 41afa9a21128..3296c10f7a39 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -81,6 +81,11 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView protected void resetState() { mSecurityMessageDisplay.setMessage(""); final boolean wasDisabled = mPasswordEntry.isEnabled(); + // Don't set enabled password entry & showSoftInput when PasswordEntry is invisible or in + // pausing stage. + if (!mResumed || !mPasswordEntry.isVisibleToUser()) { + return; + } setPasswordEntryEnabled(true); setPasswordEntryInputEnabled(true); if (wasDisabled) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index f0cdc890306f..7ae4c41d318f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -202,10 +202,6 @@ public class KeyguardStatusView extends GridLayout implements updateOwnerInfo(); updateLogoutView(); updateDark(); - - // Disable elegant text height because our fancy colon makes the ymin value huge for no - // reason. - mClockView.setElegantTextHeight(false); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java new file mode 100644 index 000000000000..db6127f1d573 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.graphics.Paint.Style; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextClock; + +import com.android.keyguard.R; +import com.android.systemui.plugins.ClockPlugin; + +import java.util.TimeZone; + +/** + * Controller for Bubble clock that can appear on lock screen and AOD. + */ +public class BubbleClockController implements ClockPlugin { + + /** + * Custom clock shown on AOD screen and behind stack scroller on lock. + */ + private View mView; + private TextClock mDigitalClock; + private ImageClock mAnalogClock; + + /** + * Small clock shown on lock screen above stack scroller. + */ + private View mLockClockContainer; + private TextClock mLockClock; + + /** + * Controller for transition to dark state. + */ + private CrossFadeDarkController mDarkController; + + private BubbleClockController() { } + + /** + * Create a BubbleClockController instance. + * + * @param layoutInflater Inflater used to inflate custom clock views. + */ + public static BubbleClockController build(LayoutInflater layoutInflater) { + BubbleClockController controller = new BubbleClockController(); + controller.createViews(layoutInflater); + return controller; + } + + private void createViews(LayoutInflater layoutInflater) { + mView = layoutInflater.inflate(R.layout.bubble_clock, null); + mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock); + mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock); + + mLockClockContainer = layoutInflater.inflate(R.layout.digital_clock, null); + mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock); + mLockClock.setVisibility(View.GONE); + + mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock); + } + + @Override + public View getView() { + return mLockClockContainer; + } + + @Override + public View getBigClockView() { + return mView; + } + + @Override + public void setStyle(Style style) {} + + @Override + public void setTextColor(int color) { + mLockClock.setTextColor(color); + mDigitalClock.setTextColor(color); + } + + @Override + public void dozeTimeTick() { + mAnalogClock.onTimeChanged(); + } + + @Override + public void setDarkAmount(float darkAmount) { + mDarkController.setDarkAmount(darkAmount); + } + + @Override + public void onTimeZoneChanged(TimeZone timeZone) { + mAnalogClock.onTimeZoneChanged(timeZone); + } + + @Override + public boolean shouldShowStatusArea() { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java new file mode 100644 index 000000000000..3591dc82c8ec --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.keyguard.R; + +/** + * Positions clock faces (analog, digital, typographic) and handles pixel shifting + * to prevent screen burn-in. + */ +public class ClockLayout extends FrameLayout { + + /** + * Clock face views. + */ + private View mDigitalClock; + private View mAnalogClock; + private View mTypeClock; + + /** + * Pixel shifting amplitidues used to prevent screen burn-in. + */ + private int mBurnInPreventionOffsetX; + private int mBurnInPreventionOffsetY; + + public ClockLayout(Context context) { + this(context, null); + } + + public ClockLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mDigitalClock = findViewById(R.id.digital_clock); + mAnalogClock = findViewById(R.id.analog_clock); + mTypeClock = findViewById(R.id.type_clock); + + // Get pixel shifting X, Y amplitudes from resources. + Resources resources = getResources(); + mBurnInPreventionOffsetX = resources.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_x); + mBurnInPreventionOffsetY = resources.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_y); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX, true); + final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY, false); + + // Put digital clock in two left corner of the screen. + if (mDigitalClock != null) { + mDigitalClock.setX(0.1f * getWidth() + offsetX); + mDigitalClock.setY(0.1f * getHeight() + offsetY); + } + + // Put the analog clock in the middle of the screen. + if (mAnalogClock != null) { + mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth())) + + offsetX); + mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) + + offsetY); + } + + // Put the typographic clock part way down the screen. + if (mTypeClock != null) { + mTypeClock.setX(offsetX); + mTypeClock.setY(0.2f * getHeight() + offsetY); + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java new file mode 100644 index 000000000000..3c3f4759614b --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.view.View; + +/** + * Controls transition to dark state by cross fading between views. + */ +final class CrossFadeDarkController { + + private final View mFadeInView; + private final View mFadeOutView; + + /** + * Creates a new controller that fades between views. + * + * @param fadeInView View to fade in when transitioning to AOD. + * @param fadeOutView View to fade out when transitioning to AOD. + */ + CrossFadeDarkController(View fadeInView, View fadeOutView) { + mFadeInView = fadeInView; + mFadeOutView = fadeOutView; + } + + /** + * Sets the amount the system has transitioned to the dark state. + * + * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen. + */ + void setDarkAmount(float darkAmount) { + mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f)); + if (darkAmount == 0f) { + mFadeInView.setVisibility(View.GONE); + } else { + if (mFadeInView.getVisibility() == View.GONE) { + mFadeInView.setVisibility(View.VISIBLE); + } + } + mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount)); + if (darkAmount == 1f) { + mFadeOutView.setVisibility(View.GONE); + } else { + if (mFadeOutView.getVisibility() == View.GONE) { + mFadeOutView.setVisibility(View.VISIBLE); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java new file mode 100644 index 000000000000..2c709e0393bd --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.content.Context; +import android.text.format.DateFormat; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.keyguard.R; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Clock composed of two images that rotate with the time. + * + * The images are the clock hands. ImageClock expects two child ImageViews + * with ids hour_hand and minute_hand. + */ +public class ImageClock extends FrameLayout { + + private ImageView mHourHand; + private ImageView mMinuteHand; + private Calendar mTime; + private String mDescFormat; + private TimeZone mTimeZone; + + public ImageClock(Context context) { + this(context, null); + } + + public ImageClock(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mTime = Calendar.getInstance(); + mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); + } + + /** + * Call when the time changes to update the rotation of the clock hands. + */ + public void onTimeChanged() { + mTime.setTimeInMillis(System.currentTimeMillis()); + final float hourAngle = mTime.get(Calendar.HOUR) * 30f; + mHourHand.setRotation(hourAngle); + final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f; + mMinuteHand.setRotation(minuteAngle); + setContentDescription(DateFormat.format(mDescFormat, mTime)); + invalidate(); + } + + /** + * Call when the time zone has changed to update clock hands. + * + * @param timeZone The updated time zone that will be used. + */ + public void onTimeZoneChanged(TimeZone timeZone) { + mTimeZone = timeZone; + mTime.setTimeZone(timeZone); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mHourHand = findViewById(R.id.hour_hand); + mMinuteHand = findViewById(R.id.minute_hand); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + onTimeChanged(); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java new file mode 100644 index 000000000000..8734754541a6 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; + +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Analog clock where the minute hand extends off of the screen. + */ +public class StretchAnalogClock extends View { + + private static final int DEFAULT_COLOR = Color.parseColor("#F5C983"); + private static final float HOUR_STROKE_WIDTH = 60f; + private static final float MINUTE_STROKE_WIDTH = 20f; + private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 80f; + + private final Paint mHourPaint = new Paint(); + private final Paint mMinutePaint = new Paint(); + private Calendar mTime; + private TimeZone mTimeZone; + + public StretchAnalogClock(Context context) { + this(context, null); + } + + public StretchAnalogClock(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + /** + * Call when the time changes to update the clock hands. + */ + public void onTimeChanged() { + mTime.setTimeInMillis(System.currentTimeMillis()); + invalidate(); + } + + /** + * Call when the time zone has changed to update clock hands. + * + * @param timeZone The updated time zone that will be used. + */ + public void onTimeZoneChanged(TimeZone timeZone) { + mTime.setTimeZone(timeZone); + } + + /** + * Set the color of the minute hand. + */ + public void setMinuteHandColor(int color) { + mMinutePaint.setColor(color); + invalidate(); + } + + private void init() { + mHourPaint.setColor(DEFAULT_COLOR); + mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH); + mHourPaint.setAntiAlias(true); + mHourPaint.setStrokeCap(Paint.Cap.ROUND); + + mMinutePaint.setColor(Color.WHITE); + mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH); + mMinutePaint.setAntiAlias(true); + mMinutePaint.setStrokeCap(Paint.Cap.ROUND); + } + + @Override + protected void onDraw(Canvas canvas) { + final float centerX = getWidth() / 2f; + final float centerY = getHeight() / 2f; + + final float minutesRotation = mTime.get(Calendar.MINUTE) * 6f; + final float hoursRotation = (mTime.get(Calendar.HOUR) * 30); + + // Compute length of clock hands. Hour hand is 60% the length from center to edge + // and minute hand is twice the length to make sure it extends past screen edge. + double sMinuteHandLengthFactor = Math.sin(2d * Math.PI * minutesRotation / 360d); + float sMinuteHandLength = (float) (2d * (centerY + (centerX - centerY) + * sMinuteHandLengthFactor * sMinuteHandLengthFactor)); + double sHourHandLengthFactor = Math.sin(2d * Math.PI * hoursRotation / 360d); + float sHourHandLength = (float) (0.6d * (centerY + (centerX - centerY) + * sHourHandLengthFactor * sHourHandLengthFactor)); + + canvas.save(); + + canvas.rotate(minutesRotation, centerX, centerY); + canvas.drawLine( + centerX, + centerY + CENTER_GAP_AND_CIRCLE_RADIUS, + centerX, + centerY - sMinuteHandLength, + mMinutePaint); + + canvas.rotate(hoursRotation - minutesRotation, centerX, centerY); + canvas.drawLine( + centerX, + centerY + CENTER_GAP_AND_CIRCLE_RADIUS, + centerX, + centerY - sHourHandLength, + mHourPaint); + + canvas.restore(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + onTimeChanged(); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java new file mode 100644 index 000000000000..0a39158cd4be --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.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 com.android.keyguard.clock; + +import android.graphics.Paint.Style; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextClock; + +import com.android.keyguard.R; +import com.android.systemui.plugins.ClockPlugin; + +import java.util.TimeZone; + +/** + * Controller for Stretch clock that can appear on lock screen and AOD. + */ +public class StretchAnalogClockController implements ClockPlugin { + + /** + * Custom clock shown on AOD screen and behind stack scroller on lock. + */ + private View mBigClockView; + private TextClock mDigitalClock; + private StretchAnalogClock mAnalogClock; + + /** + * Small clock shown on lock screen above stack scroller. + */ + private View mView; + private TextClock mLockClock; + + /** + * Controller for transition to dark state. + */ + private CrossFadeDarkController mDarkController; + + private StretchAnalogClockController() { } + + /** + * Create a BubbleClockController instance. + * + * @param layoutInflater Inflater used to inflate custom clock views. + */ + public static StretchAnalogClockController build(LayoutInflater layoutInflater) { + StretchAnalogClockController controller = new StretchAnalogClockController(); + controller.createViews(layoutInflater); + return controller; + } + + private void createViews(LayoutInflater layoutInflater) { + mBigClockView = layoutInflater.inflate(R.layout.stretchanalog_clock, null); + mAnalogClock = mBigClockView.findViewById(R.id.analog_clock); + mDigitalClock = mBigClockView.findViewById(R.id.digital_clock); + + mView = layoutInflater.inflate(R.layout.digital_clock, null); + mLockClock = mView.findViewById(R.id.lock_screen_clock); + mLockClock.setVisibility(View.GONE); + + mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock); + } + + @Override + public View getView() { + return mView; + } + + @Override + public View getBigClockView() { + return mBigClockView; + } + + @Override + public void setStyle(Style style) {} + + @Override + public void setTextColor(int color) { + mLockClock.setTextColor(color); + mDigitalClock.setTextColor(color); + mAnalogClock.setMinuteHandColor(color); + } + + @Override + public void dozeTimeTick() { + mAnalogClock.onTimeChanged(); + } + + @Override + public void setDarkAmount(float darkAmount) { + mDarkController.setDarkAmount(darkAmount); + } + + @Override + public void onTimeZoneChanged(TimeZone timeZone) { + mAnalogClock.onTimeZoneChanged(timeZone); + } + + @Override + public boolean shouldShowStatusArea() { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java new file mode 100644 index 000000000000..17d929dc8a3b --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.graphics.Paint.Style; +import android.view.LayoutInflater; +import android.view.View; + +import com.android.keyguard.R; +import com.android.systemui.plugins.ClockPlugin; + +import java.util.TimeZone; + +/** + * Plugin for a custom Typographic clock face that displays the time in words. + */ +public class TypeClockController implements ClockPlugin { + + /** + * Custom clock shown on AOD screen and behind stack scroller on lock. + */ + private View mView; + private TypographicClock mTypeClock; + + /** + * Small clock shown on lock screen above stack scroller. + */ + private View mLockClockContainer; + + /** + * Controller for transition into dark state. + */ + private CrossFadeDarkController mDarkController; + + private TypeClockController() {} + + /** + * Create a TypeClockController instance. + * + * @param inflater Inflater used to inflate custom clock views. + */ + public static TypeClockController build(LayoutInflater inflater) { + TypeClockController controller = new TypeClockController(); + controller.createViews(inflater); + return controller; + } + + private void createViews(LayoutInflater inflater) { + mView = inflater.inflate(R.layout.type_clock, null); + mTypeClock = mView.findViewById(R.id.type_clock); + + // For now, this view is used to hide the default digital clock. + // Need better transition to lock screen. + mLockClockContainer = inflater.inflate(R.layout.digital_clock, null); + mLockClockContainer.setVisibility(View.GONE); + } + + @Override + public View getView() { + return mLockClockContainer; + } + + @Override + public View getBigClockView() { + return mView; + } + + @Override + public void setStyle(Style style) {} + + @Override + public void setTextColor(int color) { + mTypeClock.setTextColor(color); + } + + @Override + public void dozeTimeTick() { + mTypeClock.onTimeChanged(); + } + + @Override + public void setDarkAmount(float darkAmount) {} + + @Override + public void onTimeZoneChanged(TimeZone timeZone) { + mTypeClock.onTimeZoneChanged(timeZone); + } + + @Override + public boolean shouldShowStatusArea() { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java new file mode 100644 index 000000000000..5f9da3ee33bb --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.content.Context; +import android.content.res.Resources; +import android.text.format.DateFormat; +import android.util.AttributeSet; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.keyguard.R; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Clock that presents the time in words. + */ +public class TypographicClock extends LinearLayout { + + private final String[] mHours; + private final String[] mMinutes; + private TextView mHeaderText; + private TextView mHourText; + private TextView mMinuteText; + private Calendar mTime; + private String mDescFormat; + private TimeZone mTimeZone; + + public TypographicClock(Context context) { + this(context, null); + } + + public TypographicClock(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mTime = Calendar.getInstance(); + mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); + Resources res = context.getResources(); + mHours = res.getStringArray(R.array.type_clock_hours); + mMinutes = res.getStringArray(R.array.type_clock_minutes); + } + + /** + * Call when the time changes to update the text of the time. + */ + public void onTimeChanged() { + mTime.setTimeInMillis(System.currentTimeMillis()); + setContentDescription(DateFormat.format(mDescFormat, mTime)); + final int hour = mTime.get(Calendar.HOUR); + mHourText.setText(mHours[hour % 12]); + final int minute = mTime.get(Calendar.MINUTE); + mMinuteText.setText(mMinutes[minute % 60]); + invalidate(); + } + + /** + * Call when the time zone has changed to update clock time. + * + * @param timeZone The updated time zone that will be used. + */ + public void onTimeZoneChanged(TimeZone timeZone) { + mTimeZone = timeZone; + mTime.setTimeZone(timeZone); + } + + /** + * Set the color of the text used to display the time. + * + * This is necessary when the wallpaper shown behind the clock on the + * lock screen changes. + */ + public void setTextColor(int color) { + mHourText.setTextColor(color); + mMinuteText.setTextColor(color); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mHeaderText = findViewById(R.id.header); + mHourText = findViewById(R.id.hour); + mMinuteText = findViewById(R.id.minute); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + onTimeChanged(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index 16e869e9d317..e28aa9d369cb 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -37,23 +37,25 @@ import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.leak.RotationUtils; -public class HardwareUiLayout extends LinearLayout implements Tunable { +/** + * Layout for placing two containers at a specific physical position on the device, relative to the + * device's hardware, regardless of screen rotation. + */ +public class HardwareUiLayout extends MultiListLayout implements Tunable { private static final String EDGE_BLEED = "sysui_hwui_edge_bleed"; private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider"; private final int[] mTmp2 = new int[2]; - private View mList; - private View mSeparatedView; + private ViewGroup mList; + private ViewGroup mSeparatedView; private int mOldHeight; private boolean mAnimating; private AnimatorSet mAnimation; private View mDivision; - private boolean mHasOutsideTouch; private HardwareBgDrawable mListBackground; private HardwareBgDrawable mSeparatedViewBackground; private Animator mAnimator; private boolean mCollapse; - private boolean mHasSeparatedButton; private int mEndPoint; private boolean mEdgeBleed; private boolean mRoundedDivider; @@ -67,6 +69,35 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { } @Override + protected ViewGroup getSeparatedView() { + return findViewById(com.android.systemui.R.id.separated_button); + } + + @Override + protected ViewGroup getListView() { + return findViewById(android.R.id.list); + } + + @Override + public void removeAllItems() { + if (mList != null) { + mList.removeAllViews(); + } + if (mSeparatedView != null) { + mSeparatedView.removeAllViews(); + } + } + + @Override + public ViewGroup getParentView(boolean separated, int index) { + if (separated) { + return getSeparatedView(); + } else { + return getListView(); + } + } + + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateSettings(); @@ -137,9 +168,9 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mList == null) { if (getChildCount() != 0) { - mList = getChildAt(0); + mList = getListView(); mList.setBackground(mListBackground); - mSeparatedView = getChildAt(1); + mSeparatedView = getSeparatedView(); mSeparatedView.setBackground(mSeparatedViewBackground); updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); mOldHeight = mList.getMeasuredHeight(); @@ -187,7 +218,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { } else { rotateLeft(); } - if (mHasSeparatedButton) { + if (mHasSeparatedView) { if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) { // Separated view has top margin, so seascape separated view need special rotation, // not a full left or right rotation. @@ -408,8 +439,8 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { if (mList == null) return; // If got separated button, setRotatedBackground to false, // all items won't get white background. - mListBackground.setRotatedBackground(mHasSeparatedButton); - mSeparatedViewBackground.setRotatedBackground(mHasSeparatedButton); + mListBackground.setRotatedBackground(mHasSeparatedView); + mSeparatedViewBackground.setRotatedBackground(mHasSeparatedView); if (mDivision != null && mDivision.getVisibility() == VISIBLE) { int index = mRotatedBackground ? 0 : 1; mDivision.getLocationOnScreen(mTmp2); @@ -460,21 +491,21 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { case RotationUtils.ROTATION_LANDSCAPE: defaultTopPadding = getPaddingLeft(); viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0; + separatedViewTopMargin = mHasSeparatedView ? params.leftMargin : 0; screenHeight = getMeasuredWidth(); targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP; break; case RotationUtils.ROTATION_SEASCAPE: defaultTopPadding = getPaddingRight(); viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0; + separatedViewTopMargin = mHasSeparatedView ? params.leftMargin : 0; screenHeight = getMeasuredWidth(); targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM; break; default: // Portrait defaultTopPadding = getPaddingTop(); viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight(); - separatedViewTopMargin = mHasSeparatedButton ? params.topMargin : 0; + separatedViewTopMargin = mHasSeparatedView ? params.topMargin : 0; screenHeight = getMeasuredHeight(); targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT; break; @@ -491,30 +522,10 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { return super.getOutlineProvider(); } - public void setOutsideTouchListener(OnClickListener onClickListener) { - mHasOutsideTouch = true; - requestLayout(); - setOnClickListener(onClickListener); - setClickable(true); - setFocusable(true); - } - public void setCollapse() { mCollapse = true; } - public void setHasSeparatedButton(boolean hasSeparatedButton) { - mHasSeparatedButton = hasSeparatedButton; - } - - public static HardwareUiLayout get(View v) { - if (v instanceof HardwareUiLayout) return (HardwareUiLayout) v; - if (v.getParent() instanceof View) { - return get((View) v.getParent()); - } - return null; - } - private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> { if (mHasOutsideTouch || (mList == null)) { inoutInfo.setTouchableInsets( diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java new file mode 100644 index 000000000000..0c7a9a9fffd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -0,0 +1,124 @@ +/* + * 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; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +/** + * Layout class representing the Global Actions menu which appears when the power button is held. + */ +public abstract class MultiListLayout extends LinearLayout { + boolean mHasOutsideTouch; + boolean mHasSeparatedView; + + int mExpectedSeparatedItemCount; + int mExpectedListItemCount; + + public MultiListLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + protected abstract ViewGroup getSeparatedView(); + + protected abstract ViewGroup getListView(); + + /** + * Removes all child items from the separated and list views, if they exist. + */ + public abstract void removeAllItems(); + + /** + * Get the parent view which will be used to contain the item at the specified index. + * @param separated Whether or not this index refers to a position in the separated or list + * container. + * @param index The index of the item within the container. + * @return The parent ViewGroup which will be used to contain the specified item + * after it has been added to the layout. + */ + public abstract ViewGroup getParentView(boolean separated, int index); + + /** + * Sets the divided view, which may have a differently-colored background. + */ + public abstract void setDivisionView(View v); + + /** + * Set the view accessibility delegate for the list view container. + */ + public void setListViewAccessibilityDelegate(View.AccessibilityDelegate delegate) { + getListView().setAccessibilityDelegate(delegate); + } + + protected void setSeparatedViewVisibility(boolean visible) { + getSeparatedView().setVisibility(visible ? View.VISIBLE : View.GONE); + } + + /** + * Sets the number of items expected to be rendered in the separated container. This allows the + * layout to correctly determine which parent containers will be used for items before they have + * beenadded to the layout. + * @param count The number of items expected. + */ + public void setExpectedSeparatedItemCount(int count) { + mExpectedSeparatedItemCount = count; + } + + /** + * Sets the number of items expected to be rendered in the list container. This allows the + * layout to correctly determine which parent containers will be used for items before they have + * beenadded to the layout. + * @param count The number of items expected. + */ + public void setExpectedListItemCount(int count) { + mExpectedListItemCount = count; + } + + /** + * Sets whether the separated view should be shown, and handles updating visibility on + * that view. + */ + public void setHasSeparatedView(boolean hasSeparatedView) { + mHasSeparatedView = hasSeparatedView; + setSeparatedViewVisibility(hasSeparatedView); + } + + /** + * Sets this layout to respond to an outside touch listener. + */ + public void setOutsideTouchListener(OnClickListener onClickListener) { + mHasOutsideTouch = true; + requestLayout(); + setOnClickListener(onClickListener); + setClickable(true); + setFocusable(true); + } + + /** + * Retrieve the MultiListLayout associated with the given view. + */ + public static MultiListLayout get(View v) { + if (v instanceof MultiListLayout) return (MultiListLayout) v; + if (v.getParent() instanceof View) { + return get((View) v.getParent()); + } + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index d7bf77da1011..957d772be730 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -284,8 +284,9 @@ public class BubbleController { @Nullable private PendingIntent getAppOverlayIntent(NotificationEntry notif) { Notification notification = notif.notification.getNotification(); - if (canLaunchInActivityView(notification.getAppOverlayIntent())) { - return notification.getAppOverlayIntent(); + if (canLaunchInActivityView(notification.getBubbleMetadata() != null + ? notification.getBubbleMetadata().getIntent() : null)) { + return notification.getBubbleMetadata().getIntent(); } else if (shouldUseContentIntent(mContext) && canLaunchInActivityView(notification.contentIntent)) { Log.d(TAG, "[addBubble " + notif.key @@ -446,15 +447,16 @@ public class BubbleController { StatusBarNotification n = entry.notification; boolean canAppOverlay = false; try { - canAppOverlay = mNotificationManagerService.areAppOverlaysAllowedForPackage( + canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage( n.getPackageName(), n.getUid()); } catch (RemoteException e) { Log.w(TAG, "Error calling NoMan to determine if app can overlay", e); } boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel( - entry.key).canOverlayApps(); - boolean hasOverlayIntent = n.getNotification().getAppOverlayIntent() != null; + entry.key).canBubble(); + boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null + && n.getNotification().getBubbleMetadata().getIntent() != null; return hasOverlayIntent && canChannelOverlay && canAppOverlay; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 4cb1feebcce4..bd7a421e9762 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -38,7 +38,13 @@ public interface DozeHost { void setAnimateWakeup(boolean animateWakeup); void setAnimateScreenOff(boolean animateScreenOff); - void onDoubleTap(float x, float y); + /** + * Reports that a tap event happend on the Sensors Low Power Island. + * + * @param x Touch X, or -1 if sensor doesn't support touch location. + * @param y Touch Y, or -1 if sensor doesn't support touch location. + */ + void onSlpiTap(float x, float y); default void setAodDimmingScrim(float scrimOpacity) {} void setDozeScreenBrightness(int value); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 50003e3a6a38..a7847739cd00 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -35,7 +35,7 @@ public class DozeLog { private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50; static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); - private static final int REASONS = 9; + private static final int REASONS = 10; public static final int PULSE_REASON_NONE = -1; public static final int PULSE_REASON_INTENT = 0; @@ -47,6 +47,7 @@ public class DozeLog { public static final int PULSE_REASON_DOCKING = 6; public static final int REASON_SENSOR_WAKE_UP = 7; public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8; + public static final int PULSE_REASON_SENSOR_TAP = 9; private static boolean sRegisterKeyguardCallback = true; @@ -207,6 +208,7 @@ public class DozeLog { case PULSE_REASON_DOCKING: return "docking"; case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen"; case REASON_SENSOR_WAKE_UP: return "wakeup"; + case PULSE_REASON_SENSOR_TAP: return "tap"; default: throw new IllegalArgumentException("bad reason: " + pulseReason); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index b6fc35553760..78374a0b1621 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -107,6 +107,13 @@ public class DozeSensors { dozeParameters.doubleTapReportsTouchCoordinates(), true /* touchscreen */), new TriggerSensor( + findSensorWithType(config.tapSensorType()), + Settings.Secure.DOZE_TAP_SCREEN_GESTURE, + true /* configured */, + DozeLog.PULSE_REASON_SENSOR_TAP, + false /* reports touch coordinates */, + true /* touchscreen */), + new TriggerSensor( findSensorWithType(config.longPressSensorType()), Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, false /* settingDef */, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 6a9b6899d509..e2e448bb0d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -132,6 +132,7 @@ public class DozeTriggers implements DozeMachine.Part { private void onSensor(int pulseReason, boolean sensorPerformedProxCheck, float screenX, float screenY, float[] rawValues) { boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP; + boolean isTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_TAP; boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS; boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP; @@ -148,8 +149,10 @@ public class DozeTriggers implements DozeMachine.Part { // In pocket, drop event. return; } - if (isDoubleTap) { - mDozeHost.onDoubleTap(screenX, screenY); + if (isDoubleTap || isTap) { + if (screenX != -1 && screenY != -1) { + mDozeHost.onSlpiTap(screenX, screenY); + } mMachine.wakeUp(); } else if (isPickup) { mMachine.wakeUp(); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 268245bd4acd..7b18fad0e105 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -67,10 +67,8 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.BaseAdapter; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; -import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.R; @@ -86,8 +84,8 @@ import com.android.internal.util.ScreenRecordHelper; import com.android.internal.util.ScreenshotHelper; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; -import com.android.systemui.HardwareUiLayout; import com.android.systemui.Interpolators; +import com.android.systemui.MultiListLayout; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.statusbar.phone.ScrimController; @@ -490,6 +488,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, public boolean showBeforeProvisioning() { return true; } + + @Override + public boolean shouldBeSeparated() { + return true; + } } private final class RestartAction extends SinglePressAction implements LongPressAction { @@ -926,6 +929,34 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return getItem(position).isEnabled(); } + public ArrayList<Action> getSeparatedActions(boolean shouldUseSeparatedView) { + ArrayList<Action> separatedActions = new ArrayList<Action>(); + if (!shouldUseSeparatedView) { + return separatedActions; + } + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + if (action.shouldBeSeparated()) { + separatedActions.add(action); + } + } + return separatedActions; + } + + public ArrayList<Action> getListActions(boolean shouldUseSeparatedView) { + if (!shouldUseSeparatedView) { + return new ArrayList<Action>(mItems); + } + ArrayList<Action> listActions = new ArrayList<Action>(); + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + if (!action.shouldBeSeparated()) { + listActions.add(action); + } + } + return listActions; + } + @Override public boolean areAllItemsEnabled() { return false; @@ -965,7 +996,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext)); // Everything but screenshot, the last item, gets white background. if (position == getCount() - 1) { - HardwareUiLayout.get(parent).setDivisionView(view); + MultiListLayout.get(parent).setDivisionView(view); } return view; } @@ -1004,6 +1035,10 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, boolean showBeforeProvisioning(); boolean isEnabled(); + + default boolean shouldBeSeparated() { + return false; + } } /** @@ -1423,9 +1458,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final Context mContext; private final MyAdapter mAdapter; - private final LinearLayout mListView; - private final FrameLayout mSeparatedView; - private final HardwareUiLayout mHardwareLayout; + private final MultiListLayout mGlobalActionsLayout; private final OnClickListener mClickListener; private final OnItemLongClickListener mLongClickListener; private final GradientDrawable mGradientDrawable; @@ -1466,16 +1499,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); setContentView(com.android.systemui.R.layout.global_actions_wrapped); - mListView = findViewById(android.R.id.list); - mSeparatedView = findViewById(com.android.systemui.R.id.separated_button); - if (!mShouldDisplaySeparatedButton) { - mSeparatedView.setVisibility(View.GONE); - } - mHardwareLayout = HardwareUiLayout.get(mListView); - mHardwareLayout.setOutsideTouchListener(view -> dismiss()); - mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton); - setTitle(R.string.global_actions); - mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() { + mGlobalActionsLayout = (MultiListLayout) + findViewById(com.android.systemui.R.id.global_actions_view); + mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); + mGlobalActionsLayout.setHasSeparatedView(mShouldDisplaySeparatedButton); + mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public boolean dispatchPopulateAccessibilityEvent( View host, AccessibilityEvent event) { @@ -1484,20 +1512,33 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return true; } }); + setTitle(R.string.global_actions); } private void updateList() { - mListView.removeAllViews(); - mSeparatedView.removeAllViews(); + mGlobalActionsLayout.removeAllItems(); + ArrayList<Action> separatedActions = + mAdapter.getSeparatedActions(mShouldDisplaySeparatedButton); + ArrayList<Action> listActions = mAdapter.getListActions(mShouldDisplaySeparatedButton); + mGlobalActionsLayout.setExpectedListItemCount(listActions.size()); + mGlobalActionsLayout.setExpectedSeparatedItemCount(separatedActions.size()); + for (int i = 0; i < mAdapter.getCount(); i++) { - ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1 - ? mSeparatedView : mListView; - View v = mAdapter.getView(i, null, parentView); + Action action = mAdapter.getItem(i); + int separatedIndex = separatedActions.indexOf(action); + ViewGroup parent; + if (separatedIndex != -1) { + parent = mGlobalActionsLayout.getParentView(true, separatedIndex); + } else { + int listIndex = listActions.indexOf(action); + parent = mGlobalActionsLayout.getParentView(false, listIndex); + } + View v = mAdapter.getView(i, null, parent); final int pos = i; v.setOnClickListener(view -> mClickListener.onClick(this, pos)); v.setOnLongClickListener(view -> mLongClickListener.onItemLongClick(null, v, pos, 0)); - parentView.addView(v); + parent.addView(v); } } @@ -1543,9 +1584,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, super.show(); mShowing = true; mGradientDrawable.setAlpha(0); - mHardwareLayout.setTranslationX(getAnimTranslation()); - mHardwareLayout.setAlpha(0); - mHardwareLayout.animate() + mGlobalActionsLayout.setTranslationX(getAnimTranslation()); + mGlobalActionsLayout.setAlpha(0); + mGlobalActionsLayout.animate() .alpha(1) .translationX(0) .setDuration(300) @@ -1564,9 +1605,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return; } mShowing = false; - mHardwareLayout.setTranslationX(0); - mHardwareLayout.setAlpha(1); - mHardwareLayout.animate() + mGlobalActionsLayout.setTranslationX(0); + mGlobalActionsLayout.setAlpha(1); + mGlobalActionsLayout.animate() .alpha(0) .translationX(getAnimTranslation()) .setDuration(300) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java index 5230cea88e8e..7ee37d567a55 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java @@ -84,7 +84,7 @@ public class SensorPrivacyTile extends QSTileImpl<BooleanState> implements @Override public Intent getLongClickIntent() { - return null; + return new Intent(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java index eea44906029d..839b06cec496 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java @@ -33,6 +33,13 @@ public interface NotificationEntryListener { default void onPendingEntryAdded(NotificationEntry entry) { } + // TODO: Combine this with onPreEntryUpdated into "onBeforeEntryFiltered" or similar + /** + * Called when a new entry is created but before it has been filtered or displayed to the user. + */ + default void onBeforeNotificationAdded(NotificationEntry entry) { + } + /** * Called when a new entry is created. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 45db00210a2e..989e781ab5ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -18,11 +18,9 @@ package com.android.systemui.statusbar.notification; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; -import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -41,7 +39,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -68,8 +65,6 @@ public class NotificationEntryManager implements @VisibleForTesting protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>(); - private final DeviceProvisionedController mDeviceProvisionedController = - Dependency.get(DeviceProvisionedController.class); private final ForegroundServiceController mForegroundServiceController = Dependency.get(ForegroundServiceController.class); @@ -81,25 +76,12 @@ public class NotificationEntryManager implements private NotificationListenerService.RankingMap mLatestRankingMap; @VisibleForTesting protected NotificationData mNotificationData; - private NotificationListContainer mListContainer; + @VisibleForTesting final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders = new ArrayList<>(); private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>(); - private final DeviceProvisionedController.DeviceProvisionedListener - mDeviceProvisionedListener = - new DeviceProvisionedController.DeviceProvisionedListener() { - @Override - public void onDeviceProvisionedChanged() { - updateNotifications(); - } - }; - - public void destroy() { - mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationEntryManager state:"); @@ -151,9 +133,6 @@ public class NotificationEntryManager implements HeadsUpManager headsUpManager) { mPresenter = presenter; mNotificationData.setHeadsUpManager(headsUpManager); - mListContainer = listContainer; - - mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); } /** Adds multiple {@link NotificationLifetimeExtender}s. */ @@ -227,7 +206,9 @@ public class NotificationEntryManager implements listener.onEntryInflated(entry, inflatedFlags); } mNotificationData.add(entry); - tagForeground(entry.notification); + for (NotificationEntryListener listener : mNotificationEntryListeners) { + listener.onBeforeNotificationAdded(entry); + } updateNotifications(); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onNotificationAdded(entry); @@ -283,7 +264,6 @@ public class NotificationEntryManager implements if (entry.rowExists()) { entry.removeRow(); - mListContainer.cleanUpViewStateForEntry(entry); } // Let's remove the children if this was a summary @@ -368,19 +348,6 @@ public class NotificationEntryManager implements } } - @VisibleForTesting - void tagForeground(StatusBarNotification notification) { - ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( - notification.getUserId(), notification.getPackageName()); - if (activeOps != null) { - int N = activeOps.size(); - for (int i = 0; i < N; i++) { - updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(), - notification.getPackageName(), true); - } - } - } - @Override public void addNotification(StatusBarNotification notification, NotificationListenerService.RankingMap ranking) { @@ -391,15 +358,6 @@ public class NotificationEntryManager implements } } - public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) { - String foregroundKey = mForegroundServiceController.getStandardLayoutKey( - UserHandle.getUserId(uid), pkg); - if (foregroundKey != null) { - mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon); - updateNotifications(); - } - } - private void updateNotificationInternal(StatusBarNotification notification, NotificationListenerService.RankingMap ranking) throws InflationException { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); @@ -452,8 +410,9 @@ public class NotificationEntryManager implements public void updateNotifications() { mNotificationData.filterAndSort(); - - mPresenter.updateNotificationViews(); + if (mPresenter != null) { + mPresenter.updateNotificationViews(); + } } public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java new file mode 100644 index 000000000000..88f4ca239af4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -0,0 +1,117 @@ +/* + * 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; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.util.ArraySet; + +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.ForegroundServiceController; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; + +/** + * Root controller for the list of notifications in the shade. + * + * TODO: Much of the code in NotificationPresenter should eventually move in here. It will proxy + * domain-specific behavior (ARC, etc) to subcontrollers. + */ +public class NotificationListController { + private final NotificationEntryManager mEntryManager; + private final NotificationListContainer mListContainer; + private final ForegroundServiceController mForegroundServiceController; + private final DeviceProvisionedController mDeviceProvisionedController; + + public NotificationListController( + NotificationEntryManager entryManager, + NotificationListContainer listContainer, + ForegroundServiceController foregroundServiceController, + DeviceProvisionedController deviceProvisionedController) { + mEntryManager = checkNotNull(entryManager); + mListContainer = checkNotNull(listContainer); + mForegroundServiceController = checkNotNull(foregroundServiceController); + mDeviceProvisionedController = checkNotNull(deviceProvisionedController); + } + + /** + * Causes the controller to register listeners on its dependencies. This method must be called + * before the controller is ready to perform its duties. + */ + public void bind() { + mEntryManager.addNotificationEntryListener(mEntryListener); + mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); + } + + /** Should be called when the list controller is being destroyed. */ + public void destroy() { + mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); + } + + @SuppressWarnings("FieldCanBeLocal") + private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { + @Override + public void onEntryRemoved( + NotificationEntry entry, + NotificationVisibility visibility, + boolean removedByUser) { + mListContainer.cleanUpViewStateForEntry(entry); + } + + @Override + public void onBeforeNotificationAdded(NotificationEntry entry) { + tagForeground(entry.notification); + } + }; + + private final DeviceProvisionedListener mDeviceProvisionedListener = + new DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + mEntryManager.updateNotifications(); + } + }; + + // TODO: This method is horrifically inefficient + private void tagForeground(StatusBarNotification notification) { + ArraySet<Integer> activeOps = + mForegroundServiceController.getAppOps( + notification.getUserId(), notification.getPackageName()); + if (activeOps != null) { + int len = activeOps.size(); + for (int i = 0; i < len; i++) { + updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(), + notification.getPackageName(), true); + } + } + } + + /** When an app op changes, propagate that change to notifications. */ + public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) { + String foregroundKey = + mForegroundServiceController.getStandardLayoutKey(UserHandle.getUserId(uid), pkg); + if (foregroundKey != null) { + mEntryManager + .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon); + mEntryManager.updateNotifications(); + } + } +} 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 31310f5f2043..32cc0e6d303a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -141,6 +141,7 @@ public class NotificationPanelView extends PanelView implements private KeyguardAffordanceHelper mAffordanceHelper; private KeyguardUserSwitcher mKeyguardUserSwitcher; private KeyguardStatusBarView mKeyguardStatusBar; + private ViewGroup mBigClockContainer; private QS mQs; private FrameLayout mQsFrame; @VisibleForTesting @@ -348,8 +349,8 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusView = findViewById(R.id.keyguard_status_view); KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container); - ViewGroup bigClockContainer = findViewById(R.id.big_clock_container); - keyguardClockSwitch.setBigClockContainer(bigClockContainer); + mBigClockContainer = findViewById(R.id.big_clock_container); + keyguardClockSwitch.setBigClockContainer(mBigClockContainer); mNotificationContainerParent = findViewById(R.id.notification_container_parent); mNotificationStackScroller = findViewById(R.id.notification_stack_scroller); @@ -585,6 +586,11 @@ public class NotificationPanelView extends PanelView implements mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y, mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock); + // Move big clock up while pulling up the bouncer + PropertyAnimator.setProperty(mBigClockContainer, AnimatableProperty.Y, + MathUtils.lerp(-mBigClockContainer.getHeight(), 0, + Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(getExpandedFraction())), + CLOCK_ANIMATION_PROPERTIES, animateClock); updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPadding; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 853d7ab9a76d..3568f2846a51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -612,9 +612,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo anim.setStartDelay(mAnimationDelay); anim.setDuration(mAnimationDuration); anim.addListener(new AnimatorListenerAdapter() { + private Callback lastCallback = mCallback; + @Override public void onAnimationEnd(Animator animation) { - onFinished(); + onFinished(lastCallback); scrim.setTag(TAG_KEY_ANIM, null); dispatchScrimsVisible(); @@ -672,14 +674,23 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } private void onFinished() { + onFinished(mCallback); + } + + private void onFinished(Callback callback) { if (mWakeLockHeld) { mWakeLock.release(); mWakeLockHeld = false; } - if (mCallback != null) { - mCallback.onFinished(); - mCallback = null; + + if (callback != null) { + callback.onFinished(); + + if (callback == mCallback) { + mCallback = null; + } } + // When unlocking with fingerprint, we'll fade the scrims from black to transparent. // At the end of the animation we need to remove the tint. if (mState == ScrimState.UNLOCKED) { 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 514bb228cd97..ed71598e22ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -196,6 +196,7 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.NotificationListController; import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -387,6 +388,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NotificationGutsManager mGutsManager; protected NotificationLogger mNotificationLogger; protected NotificationEntryManager mEntryManager; + private NotificationListController mNotificationListController; private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private NotificationRowBinder mNotificationRowBinder; protected NotificationViewHierarchyManager mViewHierarchyManager; @@ -593,7 +595,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { mForegroundServiceController.onAppOpChanged(code, uid, packageName, active); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { - mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active); + mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active); }); } @@ -1044,6 +1046,13 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager, mNotificationAlertingManager); + mNotificationListController = + new NotificationListController( + mEntryManager, + (NotificationListContainer) mStackScroller, + mForegroundServiceController, + mDeviceProvisionedController); + mAppOpsController.addCallback(APP_OPS, this); mNotificationListener.setUpWithPresenter(mPresenter); mNotificationShelf.setOnActivatedListener(mPresenter); @@ -1056,6 +1065,7 @@ public class StatusBar extends SystemUI implements DemoMode, this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); + mNotificationListController.bind(); } /** @@ -2831,7 +2841,7 @@ public class StatusBar extends SystemUI implements DemoMode, } catch (RemoteException e) { // Ignore. } - mEntryManager.destroy(); + mNotificationListController.destroy(); // End old BaseStatusBar.destroy(). if (mStatusBarWindow != null) { mWindowManager.removeViewImmediate(mStatusBarWindow); @@ -3999,7 +4009,7 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void onDoubleTap(float screenX, float screenY) { + public void onSlpiTap(float screenX, float screenY) { if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 9422101c76dd..f79ad71a114f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -541,7 +541,8 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting void doUpdateMobileControllers() { - List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList(); + List<SubscriptionInfo> subscriptions = mSubscriptionManager + .getActiveSubscriptionInfoList(true); if (subscriptions == null) { subscriptions = Collections.emptyList(); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java new file mode 100644 index 000000000000..9e946faf2a52 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class BubbleClockControllerTest extends SysuiTestCase { + + private BubbleClockController mClockController; + + @Before + public void setUp() { + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); + mClockController = BubbleClockController.build(layoutInflater); + } + + @Test + public void setDarkAmount_fadeIn() { + ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); + View smallClock = smallClockFrame.getChildAt(0); + // WHEN dark amount is set to AOD + mClockController.setDarkAmount(1f); + // THEN small clock should not be shown. + assertThat(smallClock.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void setTextColor_setDigitalClock() { + ViewGroup smallClock = (ViewGroup) mClockController.getView(); + // WHEN text color is set + mClockController.setTextColor(42); + // THEN child of small clock should have text color set. + TextView digitalClock = (TextView) smallClock.getChildAt(0); + assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java new file mode 100644 index 000000000000..fd7657ff18cc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class CrossFadeDarkControllerTest extends SysuiTestCase { + + private View mViewFadeIn; + private View mViewFadeOut; + private CrossFadeDarkController mDarkController; + + @Before + public void setUp() { + mViewFadeIn = new TextView(getContext()); + mViewFadeIn.setVisibility(View.VISIBLE); + mViewFadeIn.setAlpha(1f); + mViewFadeOut = new TextView(getContext()); + mViewFadeOut.setVisibility(View.VISIBLE); + mViewFadeOut.setAlpha(1f); + + mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut); + } + + @Test + public void setDarkAmount_fadeIn() { + // WHEN dark amount corresponds to AOD + mDarkController.setDarkAmount(1f); + // THEN fade in view should be faded in and fade out view faded out. + assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f); + assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f); + assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void setDarkAmount_fadeOut() { + // WHEN dark amount corresponds to lock screen + mDarkController.setDarkAmount(0f); + // THEN fade out view should bed faded out and fade in view faded in. + assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f); + assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f); + assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setDarkAmount_partialFadeIn() { + // WHEN dark amount corresponds to a partial transition + mDarkController.setDarkAmount(0.9f); + // THEN views should have intermediate alpha value. + assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f); + assertThat(mViewFadeIn.getAlpha()).isLessThan(1f); + assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setDarkAmount_partialFadeOut() { + // WHEN dark amount corresponds to a partial transition + mDarkController.setDarkAmount(0.1f); + // THEN views should have intermediate alpha value. + assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f); + assertThat(mViewFadeOut.getAlpha()).isLessThan(1f); + assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java new file mode 100644 index 000000000000..8de8f3d0674d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class StretchAnalogClockControllerTest extends SysuiTestCase { + + private StretchAnalogClockController mClockController; + + @Before + public void setUp() { + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); + mClockController = StretchAnalogClockController.build(layoutInflater); + } + + @Test + public void setDarkAmount_fadeIn() { + ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); + View smallClock = smallClockFrame.getChildAt(0); + // WHEN dark amount is set to AOD + mClockController.setDarkAmount(1f); + // THEN small clock should not be shown. + assertThat(smallClock.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void setTextColor_setDigitalClock() { + ViewGroup smallClock = (ViewGroup) mClockController.getView(); + // WHEN text color is set + mClockController.setTextColor(42); + // THEN child of small clock should have text color set. + TextView digitalClock = (TextView) smallClock.getChildAt(0); + assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java index ce28b50436eb..dc4287287b03 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java @@ -104,7 +104,7 @@ class DozeHostFake implements DozeHost { } @Override - public void onDoubleTap(float x, float y) { + public void onSlpiTap(float x, float y) { doubleTapX = y; doubleTapY = y; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java index 458377017fb9..3415c72230e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java @@ -91,7 +91,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mMockPm = mock(PackageManager.class); mMockListener = mock(PluginListener.class); mMockManager = mock(PluginManagerImpl.class); - when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader()); + when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader()); mMockEnabler = mock(PluginEnabler.class); when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler); mMockVersionInfo = mock(VersionInfo.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index 76e68f1df724..536c043bd7ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -25,6 +25,7 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; @@ -135,9 +136,13 @@ public class PluginManagerTest extends SysuiTestCase { }); resetExceptionHandler(); + String sourceDir = "myPlugin"; + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.sourceDir = sourceDir; + applicationInfo.packageName = WHITELISTED_PACKAGE; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - assertNull(mPluginManager.getOneShotPlugin("myPlugin", TestPlugin.class)); - assertNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE)); + assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class)); + assertNull(mPluginManager.getClassLoader(applicationInfo)); } @Test @@ -152,9 +157,16 @@ public class PluginManagerTest extends SysuiTestCase { }); resetExceptionHandler(); + String sourceDir = "myPlugin"; + ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo(); + whiteListedApplicationInfo.sourceDir = sourceDir; + whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE; + ApplicationInfo invalidApplicationInfo = new ApplicationInfo(); + invalidApplicationInfo.sourceDir = sourceDir; + invalidApplicationInfo.packageName = "com.android.invalidpackage"; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - assertNotNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE)); - assertNull(mPluginManager.getClassLoader("myPlugin", "com.android.invalidpackage")); + assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo)); + assertNull(mPluginManager.getClassLoader(invalidApplicationInfo)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index 2b13f864b9a5..89525944b685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -27,6 +27,7 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Icon; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; @@ -220,8 +221,7 @@ public class NotificationTestHelper { notificationBuilder.setGroup(groupKey); } if (isBubble) { - PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); - notificationBuilder.setAppOverlayIntent(bubbleIntent); + notificationBuilder.setBubbleMetadata(makeBubbleMetadata()); } return notificationBuilder.build(); } @@ -282,4 +282,14 @@ public class NotificationTestHelper { mGroupManager.onEntryAdded(entry); return row; } + + private Notification.BubbleMetadata makeBubbleMetadata() { + PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + return new Notification.BubbleMetadata.Builder() + .setIntent(bubbleIntent) + .setTitle("bubble title") + .setIcon(Icon.createWithResource(mContext, 1)) + .setDesiredHeight(314) + .build(); + } } 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 d937f93482d5..9ce6ae139998 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 @@ -22,19 +22,15 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -49,7 +45,6 @@ import android.service.notification.StatusBarNotification; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.ArraySet; import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; @@ -79,8 +74,6 @@ import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import junit.framework.Assert; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -346,7 +339,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { verify(mEntryListener, never()).onInflationError(any(), any()); - verify(mListContainer).cleanUpViewStateForEntry(mEntry); verify(mPresenter).updateNotificationViews(); verify(mEntryListener).onEntryRemoved( mEntry, null, false /* removedByUser */); @@ -401,90 +393,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @Test - public void testUpdateAppOps_foregroundNoti() { - com.android.systemui.util.Assert.isNotMainThread(); - - when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) - .thenReturn(mEntry.key); - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - - mEntryManager.updateNotificationsForAppOp( - AppOpsManager.OP_CAMERA, mEntry.notification.getUid(), - mEntry.notification.getPackageName(), true); - - verify(mPresenter, times(1)).updateNotificationViews(); - assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains( - AppOpsManager.OP_CAMERA)); - } - - @Test - public void testUpdateAppOps_otherNoti() { - com.android.systemui.util.Assert.isNotMainThread(); - - when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) - .thenReturn(null); - mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true); - - verify(mPresenter, never()).updateNotificationViews(); - } - - @Test - public void testAddNotificationExistingAppOps() { - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - ArraySet<Integer> expected = new ArraySet<>(); - expected.add(3); - expected.add(235); - expected.add(1); - - when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(expected); - when(mForegroundServiceController.getStandardLayoutKey( - mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(mEntry.key); - - mEntryManager.tagForeground(mEntry.notification); - - Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size()); - for (int op : expected) { - assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op)); - } - } - - @Test - public void testAdd_noExistingAppOps() { - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - when(mForegroundServiceController.getStandardLayoutKey( - mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(mEntry.key); - when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(null); - - mEntryManager.tagForeground(mEntry.notification); - Assert.assertEquals(0, mEntry.mActiveAppOps.size()); - } - - @Test - public void testAdd_existingAppOpsNotForegroundNoti() { - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - ArraySet<Integer> ops = new ArraySet<>(); - ops.add(3); - ops.add(235); - ops.add(1); - when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(ops); - when(mForegroundServiceController.getStandardLayoutKey( - mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn("something else"); - - mEntryManager.tagForeground(mEntry.notification); - Assert.assertEquals(0, mEntry.mActiveAppOps.size()); - } - - @Test public void testUpdateNotificationRanking() { when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); when(mEnvironment.isDeviceProvisioned()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java new file mode 100644 index 000000000000..4b5037bb3f64 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java @@ -0,0 +1,241 @@ +/* + * 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; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.app.Notification; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.ArraySet; + +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.ForegroundServiceController; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationListControllerTest extends SysuiTestCase { + private NotificationListController mController; + + @Mock private NotificationEntryManager mEntryManager; + @Mock private NotificationListContainer mListContainer; + @Mock private ForegroundServiceController mForegroundServiceController; + @Mock private DeviceProvisionedController mDeviceProvisionedController; + + @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor; + @Captor private ArgumentCaptor<DeviceProvisionedListener> mProvisionedCaptor; + + private NotificationEntryListener mEntryListener; + private DeviceProvisionedListener mProvisionedListener; + + // TODO: Remove this once EntryManager no longer needs to be mocked + private NotificationData mNotificationData = new NotificationData(); + + private int mNextNotifId = 0; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); + + mController = new NotificationListController( + mEntryManager, + mListContainer, + mForegroundServiceController, + mDeviceProvisionedController); + mController.bind(); + + // Capture callbacks passed to mocks + verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture()); + mEntryListener = mEntryListenerCaptor.getValue(); + verify(mDeviceProvisionedController).addCallback(mProvisionedCaptor.capture()); + mProvisionedListener = mProvisionedCaptor.getValue(); + } + + @Test + public void testCleanUpViewStateOnEntryRemoved() { + final NotificationEntry entry = buildEntry(); + mEntryListener.onEntryRemoved( + entry, + NotificationVisibility.obtain(entry.key, 0, 0, true), + false); + verify(mListContainer).cleanUpViewStateForEntry(entry); + } + + @Test + public void testCallUpdateNotificationsOnDeviceProvisionedChange() { + mProvisionedListener.onDeviceProvisionedChanged(); + verify(mEntryManager).updateNotifications(); + } + + @Test + public void testAppOps_appOpAddedToForegroundNotif() { + // GIVEN a notification associated with a foreground service + final NotificationEntry entry = buildEntry(); + mNotificationData.add(entry); + when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) + .thenReturn(entry.key); + + // WHEN we are notified of a new app op + mController.updateNotificationsForAppOp( + AppOpsManager.OP_CAMERA, + entry.notification.getUid(), + entry.notification.getPackageName(), + true); + + // THEN the app op is added to the entry + assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + // THEN updateNotifications() is called + verify(mEntryManager, times(1)).updateNotifications(); + } + + @Test + public void testAppOps_appOpAddedToUnrelatedNotif() { + // GIVEN No current foreground notifs + when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) + .thenReturn(null); + + // WHEN An unrelated notification gets a new app op + mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true); + + // THEN We never call updateNotifications() + verify(mEntryManager, never()).updateNotifications(); + } + + @Test + public void testAppOps_addNotificationWithExistingAppOps() { + // GIVEN a notification with three associated app ops that is associated with a foreground + // service + final NotificationEntry entry = buildEntry(); + mNotificationData.add(entry); + ArraySet<Integer> expected = new ArraySet<>(); + expected.add(3); + expected.add(235); + expected.add(1); + when(mForegroundServiceController.getStandardLayoutKey( + entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(entry.key); + when(mForegroundServiceController.getAppOps(entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(expected); + + // WHEN the notification is added + mEntryListener.onBeforeNotificationAdded(entry); + + // THEN the entry is tagged with all three app ops + assertEquals(expected.size(), entry.mActiveAppOps.size()); + for (int op : expected) { + assertTrue("Entry missing op " + op, entry.mActiveAppOps.contains(op)); + } + } + + @Test + public void testAdd_addNotificationWithNoExistingAppOps() { + // GIVEN a notification with NO associated app ops + final NotificationEntry entry = buildEntry(); + + mNotificationData.add(entry); + when(mForegroundServiceController.getStandardLayoutKey( + entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(entry.key); + when(mForegroundServiceController.getAppOps(entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(null); + + // WHEN the notification is added + mEntryListener.onBeforeNotificationAdded(entry); + + // THEN the entry doesn't have any app ops associated with it + assertEquals(0, entry.mActiveAppOps.size()); + } + + @Test + public void testAdd_addNonForegroundNotificationWithExistingAppOps() { + // GIVEN a notification with app ops that isn't associated with a foreground service + final NotificationEntry entry = buildEntry(); + mNotificationData.add(entry); + ArraySet<Integer> ops = new ArraySet<>(); + ops.add(3); + ops.add(235); + ops.add(1); + when(mForegroundServiceController.getAppOps(entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(ops); + when(mForegroundServiceController.getStandardLayoutKey( + entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn("something else"); + + // WHEN the notification is added + mEntryListener.onBeforeNotificationAdded(entry); + + // THEN the entry doesn't have any app ops associated with it + assertEquals(0, entry.mActiveAppOps.size()); + } + + private NotificationEntry buildEntry() { + mNextNotifId++; + + Notification.Builder n = new Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text"); + + StatusBarNotification notification = + new StatusBarNotification( + TEST_PACKAGE_NAME, + TEST_PACKAGE_NAME, + mNextNotifId, + null, + TEST_UID, + 0, + n.build(), + new UserHandle(ActivityManager.getCurrentUser()), + null, + 0); + return new NotificationEntry(notification); + } + + private static final String TEST_PACKAGE_NAME = "test"; + private static final int TEST_UID = 0; +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index fdbf09083a4e..c1f88855ac24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -182,6 +182,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { subs.add(subscription); } when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs); + when(mMockSm.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subs); mNetworkController.doUpdateMobileControllers(); } diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk index 295b3d8bc979..be9ba4351056 100644 --- a/packages/services/PacProcessor/Android.mk +++ b/packages/services/PacProcessor/Android.mk @@ -26,6 +26,6 @@ LOCAL_PACKAGE_NAME := PacProcessor LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform -LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor libpac +LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor include $(BUILD_PACKAGE) diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 44edb568573c..a07411d0afaf 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6801,8 +6801,20 @@ message MetricsEvent { // ACTION: Tap & Pay -> Default Application Setting -> Use Default // OS: Q ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623; - // ---- End Q Constants, all Q constants go above this line ---- + // OPEN: Settings > System > Input & Gesture > Skip song gesture + // OS: Q + SETTINGS_GESTURE_SKIP_SONG = 1624; + + // OPEN: Settings > System > Input & Gesture > Silence gesture + // OS: Q + SETTINGS_GESTURE_SILENCE = 1625; + + // OPEN: Settings > System > Input & Gesture > Tap screen gesture + // OS: Q + SETTINGS_GESTURE_TAP_SCREEN = 1626; + + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index cf9f233b8fe2..8ffaddefd3ef 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -462,7 +462,7 @@ class TouchExplorer extends BaseEventStreamTransformation return false; } - endGestureDetection(); + endGestureDetection(true); mAms.onGesture(gestureId); @@ -472,7 +472,7 @@ class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onGestureCancelled(MotionEvent event, int policyFlags) { if (mCurrentState == STATE_GESTURE_DETECTING) { - endGestureDetection(); + endGestureDetection(event.getActionMasked() == MotionEvent.ACTION_UP); return true; } else if (mCurrentState == STATE_TOUCH_EXPLORING) { // If the finger is still moving, pass the event on. @@ -804,13 +804,19 @@ class TouchExplorer extends BaseEventStreamTransformation } } - private void endGestureDetection() { + private void endGestureDetection(boolean interactionEnd) { mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); - // Announce the end of a the touch interaction. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + if (interactionEnd) { + // Announce the end of a the touch interaction. + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + } else { + // If gesture detection is end, but user doesn't release the finger, announce the + // transition to exploration state. + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); + } mExitGestureDetectionModeDelayed.cancel(); mCurrentState = STATE_TOUCH_EXPLORING; @@ -889,7 +895,6 @@ class TouchExplorer extends BaseEventStreamTransformation MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); } } @@ -1148,8 +1153,8 @@ class TouchExplorer extends BaseEventStreamTransformation sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Clearing puts is in touch exploration state with a finger already // down, so announce the transition to exploration state. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); clear(); + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); } } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 24fd7b9ae71d..c992da43fc07 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -252,9 +252,8 @@ public final class AutofillManagerService @Override // from AbstractMasterSystemService protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { - return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory, - mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, - disabled); + return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, + mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled); } @Override // AbstractMasterSystemService @@ -291,6 +290,13 @@ public final class AutofillManagerService return mSupportedSmartSuggestionModes; } + /** + * Logs a request so it's dumped later... + */ + void logRequestLocked(@NonNull String historyItem) { + mRequestsHistory.log(historyItem); + } + // Called by AutofillManagerServiceImpl, doesn't need to check permission boolean isInstantServiceAllowed() { return mAllowInstantService; @@ -701,6 +707,11 @@ public final class AutofillManagerService public void onBackKeyPressed() { if (sDebug) Slog.d(TAG, "onBackKeyPressed()"); mUi.hideAll(null); + synchronized (mLock) { + final AutofillManagerServiceImpl service = + getServiceForUserLocked(UserHandle.getCallingUserId()); + service.onBackKeyPressed(); + } } @Override diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index a6bb049602a0..954b67e4e2dc 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -108,7 +108,6 @@ final class AutofillManagerServiceImpl private static final Random sRandom = new Random(); - private final LocalLog mRequestsHistory; private final LocalLog mUiLatencyHistory; private final LocalLog mWtfHistory; private final FieldClassificationStrategy mFieldClassificationStrategy; @@ -166,12 +165,12 @@ final class AutofillManagerServiceImpl @Nullable private RemoteAugmentedAutofillService mRemoteAugmentedAutofillService; - AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory, + AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui, - AutofillCompatState autofillCompatState, boolean disabled) { + AutofillCompatState autofillCompatState, + boolean disabled) { super(master, lock, userId); - mRequestsHistory = requestsHistory; mUiLatencyHistory = uiLatencyHistory; mWtfHistory = wtfHistory; mUi = ui; @@ -187,6 +186,15 @@ final class AutofillManagerServiceImpl } @GuardedBy("mLock") + void onBackKeyPressed() { + final RemoteAugmentedAutofillService remoteService = + getRemoteAugmentedAutofillServiceLocked(); + if (remoteService != null) { + remoteService.onDestroyAutofillWindowsRequest(); + } + } + + @GuardedBy("mLock") @Override // from PerUserSystemService protected boolean updateLocked(boolean disabled) { destroySessionsLocked(); @@ -301,7 +309,7 @@ final class AutofillManagerServiceImpl + " s=" + mInfo.getServiceInfo().packageName + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" + hasCallback + " f=" + flags; - mRequestsHistory.log(historyItem); + mMaster.logRequestLocked(historyItem); newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags); diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index a8ff9b0d5a3f..5d8d8fa46d3f 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -109,8 +109,8 @@ final class RemoteAugmentedAutofillService /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ - public void onDestroyAutofillWindowsRequest(int sessionId) { - scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId)); + public void onDestroyAutofillWindowsRequest() { + scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest()); } // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index cf4963c1bebf..7dfd8fef13c2 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2610,6 +2610,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + " when server returned null for session " + this.id); } + final String historyItem = + "aug:id=" + id + " u=" + uid + " m=" + mode + + " a=" + ComponentName.flattenToShortString(mComponentName) + + " f=" + mCurrentViewId + + " s=" + remoteService.getComponentName(); + mService.getMaster().logRequestLocked(historyItem); + final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue(); // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize @@ -2619,7 +2626,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentValue); if (mAugmentedAutofillDestroyer == null) { - mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id); + mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(); } return mAugmentedAutofillDestroyer; } diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 529430ca4eaf..a7bada07ffa8 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -562,7 +562,7 @@ public class TransportManager { private void registerTransportsFromPackage( String packageName, Predicate<ComponentName> transportComponentFilter) { try { - mPackageManager.getPackageInfo(packageName, 0); + mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Trying to register transports from package not found " + packageName); return; @@ -599,7 +599,8 @@ public class TransportManager { return false; } try { - PackageInfo packInfo = mPackageManager.getPackageInfo(transport.getPackageName(), 0); + PackageInfo packInfo = + mPackageManager.getPackageInfoAsUser(transport.getPackageName(), 0, mUserId); if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) { Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged"); diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 3a8966a04055..79f8a7e4e9ae 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -494,20 +494,18 @@ public class UserBackupManagerService { mUserId); mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null"); - mBaseStateDir.mkdirs(); - if (!SELinux.restorecon(mBaseStateDir)) { - Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir); + // TODO (b/120424138): Remove once the system user is migrated to use the per-user CE + // directory. Per-user CE directories are managed by vold. + if (userId == UserHandle.USER_SYSTEM) { + mBaseStateDir.mkdirs(); + if (!SELinux.restorecon(mBaseStateDir)) { + Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir); + } } + // TODO (b/120424138): The system user currently uses the cache which is managed by init.rc + // Initialization and restorecon is managed by vold for per-user CE directories. mDataDir = checkNotNull(dataDir, "dataDir cannot be null"); - // TODO(b/120424138): Remove when the system user moves out of the cache dir. The cache dir - // is managed by init.rc so we don't have to create it below. - if (userId != UserHandle.USER_SYSTEM) { - mDataDir.mkdirs(); - if (!SELinux.restorecon(mDataDir)) { - Slog.w(TAG, "SELinux restorecon failed on " + mDataDir); - } - } mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng); // Receivers for scheduled backups and transport initialization operations. @@ -584,7 +582,7 @@ public class UserBackupManagerService { mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS); // Power management - mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); + mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*-" + userId); } void initializeBackupEnableState() { @@ -1352,7 +1350,7 @@ public class UserBackupManagerService { private List<PackageInfo> allAgentPackages() { // !!! TODO: cache this and regenerate only when necessary int flags = PackageManager.GET_SIGNING_CERTIFICATES; - List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags); + List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(flags, mUserId); int numPackages = packages.size(); for (int a = numPackages - 1; a >= 0; a--) { PackageInfo pkg = packages.get(a); @@ -1366,8 +1364,8 @@ public class UserBackupManagerService { // we will need the shared library path, so look that up and store it here. // This is used implicitly when we pass the PackageInfo object off to // the Activity Manager to launch the app for backup/restore purposes. - app = mPackageManager.getApplicationInfo(pkg.packageName, - PackageManager.GET_SHARED_LIBRARY_FILES); + app = mPackageManager.getApplicationInfoAsUser(pkg.packageName, + PackageManager.GET_SHARED_LIBRARY_FILES, mUserId); pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles; pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos; } @@ -1392,7 +1390,7 @@ public class UserBackupManagerService { notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | Intent.FLAG_RECEIVER_FOREGROUND); notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName); - mContext.sendBroadcastAsUser(notification, UserHandle.OWNER); + mContext.sendBroadcastAsUser(notification, UserHandle.of(mUserId)); } mProcessedPackagesJournal.addPackage(packageName); @@ -2208,11 +2206,10 @@ public class UserBackupManagerService { /** Used by both incremental and full restore to restore widget data. */ public void restoreWidgetData(String packageName, byte[] widgetData) { // Apply the restored widget state and generate the ID update for the app - // TODO: http://b/22388012 if (MORE_DEBUG) { Slog.i(TAG, "Incorporating restored widget data"); } - AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM); + AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId); } // ***************************** @@ -2291,20 +2288,6 @@ public class UserBackupManagerService { /** Sent from an app's backup agent to let the service know that there's new data to backup. */ public void dataChanged(final String packageName) { - final int callingUserHandle = UserHandle.getCallingUserId(); - if (callingUserHandle != UserHandle.USER_SYSTEM) { - // TODO: http://b/22388012 - // App is running under a non-owner user profile. For now, we do not back - // up data from secondary user profiles. - // TODO: backups for all user profiles although don't add backup for profiles - // without adding admin control in DevicePolicyManager. - if (MORE_DEBUG) { - Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " - + callingUserHandle); - } - return; - } - final HashSet<String> targets = dataChangedTargets(packageName); if (targets == null) { Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" @@ -2921,7 +2904,7 @@ public class UserBackupManagerService { try { int transportUid = mContext.getPackageManager() - .getPackageUid(transportComponent.getPackageName(), 0); + .getPackageUidAsUser(transportComponent.getPackageName(), 0, mUserId); if (callingUid != transportUid) { throw new SecurityException("Only the transport can change its description"); } diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index 862ca711e694..cfc129e11c6e 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -45,6 +45,7 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.SELinux; +import android.os.UserHandle; import android.os.WorkSource; import com.android.internal.annotations.GuardedBy; @@ -686,8 +687,12 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { ParcelFileDescriptor.open( mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); - if (!SELinux.restorecon(mBackupDataFile)) { - mReporter.onRestoreconFailed(mBackupDataFile); + // TODO (b/120424138): Remove once the system user is migrated to use the per-user CE + // directory. Per-user CE directories are managed by vold. + if (mUserId == UserHandle.USER_SYSTEM) { + if (!SELinux.restorecon(mBackupDataFile)) { + mReporter.onRestoreconFailed(mBackupDataFile); + } } IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.extractAgentData()"); diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index c7f3315493b4..b3d9fbcb88d3 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -68,6 +68,8 @@ import java.util.List; public class FullRestoreEngine extends RestoreEngine { private final UserBackupManagerService mBackupManagerService; + private final int mUserId; + // Task in charge of monitoring timeouts private final BackupRestoreTask mMonitorTask; @@ -146,6 +148,7 @@ public class FullRestoreEngine extends RestoreEngine { backupManagerService.getAgentTimeoutParameters(), "Timeout parameters cannot be null"); mIsAdbRestore = isAdbRestore; + mUserId = backupManagerService.getUserId(); } public IBackupAgent getAgent() { @@ -272,7 +275,7 @@ public class FullRestoreEngine extends RestoreEngine { instream, mBackupManagerService.getContext(), mDeleteObserver, mManifestSignatures, mPackagePolicies, info, installerPackageName, - bytesReadListener, mBackupManagerService.getUserId()); + bytesReadListener, mUserId); // good to go; promote to ACCEPT mPackagePolicies.put(pkg, isSuccessfullyInstalled ? RestorePolicy.ACCEPT @@ -329,9 +332,8 @@ public class FullRestoreEngine extends RestoreEngine { } try { - mTargetApp = - mBackupManagerService.getPackageManager().getApplicationInfo( - pkg, 0); + mTargetApp = mBackupManagerService.getPackageManager() + .getApplicationInfoAsUser(pkg, 0, mUserId); // If we haven't sent any data to this app yet, we probably // need to clear it first. Check that. @@ -684,7 +686,7 @@ public class FullRestoreEngine extends RestoreEngine { String packageListString = Settings.Secure.getStringForUser( mBackupManagerService.getContext().getContentResolver(), Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE, - mBackupManagerService.getUserId()); + mUserId); if (TextUtils.isEmpty(packageListString)) { return false; } diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 5284d94c2aa7..d01f77bfd84c 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -360,7 +360,6 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // If we're starting a full-system restore, set up to begin widget ID remapping if (mIsSystemRestore) { - // TODO: http://b/22388012 AppWidgetBackupBridge.restoreStarting(mUserId); } @@ -1079,7 +1078,6 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } // Kick off any work that may be needed regarding app widget restores - // TODO: http://b/22388012 AppWidgetBackupBridge.restoreFinished(mUserId); // If this was a full-system restore, record the ancestral diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index dc0f6028b0f8..e4bbcd67d4df 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -32,6 +32,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; +import android.util.LocalLog; import android.util.Slog; import android.view.contentcapture.IContentCaptureManager; @@ -69,6 +70,8 @@ public final class ContentCaptureManagerService extends private final LocalService mLocalService = new LocalService(); + private final LocalLog mRequestsHistory = new LocalLog(20); + public ContentCaptureManagerService(@NonNull Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, com.android.internal.R.string.config_defaultContentCaptureService), @@ -154,6 +157,13 @@ public final class ContentCaptureManagerService extends } } + /** + * Logs a request so it's dumped later... + */ + void logRequestLocked(@NonNull String historyItem) { + mRequestsHistory.log(historyItem); + } + private ActivityManagerInternal getAmInternal() { synchronized (mLock) { if (mAm == null) { @@ -217,9 +227,29 @@ public final class ContentCaptureManagerService extends public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; + boolean showHistory = true; + if (args != null) { + for (String arg : args) { + switch(arg) { + case "--no-history": + showHistory = false; + break; + case "--help": + pw.println("Usage: dumpsys content_capture [--no-history]"); + return; + default: + Slog.w(TAG, "Ignoring invalid dump arg: " + arg); + } + } + } + synchronized (mLock) { dumpLocked("", pw); } + if (showHistory) { + pw.println(); pw.println("Requests history:"); pw.println(); + mRequestsHistory.reverseDump(fd, pw, args); + } } @Override diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 09aa4212654e..8d2c79bd9923 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -17,6 +17,9 @@ package com.android.server.contentcapture; import static android.service.contentcapture.ContentCaptureService.setClientState; +import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; +import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID; +import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; @@ -36,10 +39,11 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.service.contentcapture.ContentCaptureService; +import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.util.ArrayMap; +import android.util.Log; import android.util.Slog; -import android.view.contentcapture.ContentCaptureSession; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -48,6 +52,7 @@ import com.android.server.infra.AbstractPerUserSystemService; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; /** * Per-user instance of {@link ContentCaptureManagerService}. @@ -72,12 +77,14 @@ final class ContentCapturePerUserService @GuardedBy("mLock") private RemoteContentCaptureService mRemoteService; + private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback = + new ContentCaptureServiceRemoteCallback(); + // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's ContentCapturePerUserService(@NonNull ContentCaptureManagerService master, @NonNull Object lock, boolean disabled, @UserIdInt int userId) { super(master, lock, userId); - updateRemoteServiceLocked(disabled); } @@ -100,10 +107,10 @@ final class ContentCapturePerUserService } if (!disabled) { - mRemoteService = new RemoteContentCaptureService( - mMaster.getContext(), - ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, mUserId, this, - mMaster.isBindInstantServiceAllowed(), mMaster.verbose); + mRemoteService = new RemoteContentCaptureService(mMaster.getContext(), + ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, + mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(), + mMaster.verbose); } } @@ -162,14 +169,24 @@ final class ContentCapturePerUserService @NonNull ComponentName componentName, int taskId, int displayId, @NonNull String sessionId, int uid, int flags, @NonNull IResultReceiver clientReceiver) { - if (!isEnabledLocked()) { + + final ComponentName serviceComponentName = getServiceComponentName(); + final boolean enabled = isEnabledLocked(); + final String historyItem = + "id=" + sessionId + " uid=" + uid + + " a=" + ComponentName.flattenToShortString(componentName) + + " t=" + taskId + " d=" + displayId + + " s=" + ComponentName.flattenToShortString(serviceComponentName) + + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)"); + mMaster.logRequestLocked(historyItem); + + if (!enabled) { // TODO: it would be better to split in differet reasons, like // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY - setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE, + setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE, /* binder= */ null); return; } - final ComponentName serviceComponentName = getServiceComponentName(); if (serviceComponentName == null) { // TODO(b/111276913): this happens when the system service is starting, we should // probably handle it in a more elegant way (like waiting for boot_complete or @@ -184,7 +201,7 @@ final class ContentCapturePerUserService if (existingSession != null) { Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken + ": ignoring because it already exists for " + existingSession.mActivityToken); - setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID, + setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID, /* binder=*/ null); return; } @@ -197,8 +214,7 @@ final class ContentCapturePerUserService // TODO(b/119613670): log metrics Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken + ": ignoring because service is not set"); - // TODO(b/111276913): use a new disabled state? - setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE, + setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE, /* binder= */ null); return; } @@ -338,4 +354,50 @@ final class ContentCapturePerUserService } return null; } + + private final class ContentCaptureServiceRemoteCallback extends + IContentCaptureServiceCallback.Stub { + + @Override + public void setContentCaptureWhitelist(List<String> packages, + List<ComponentName> activities) { + if (mMaster.verbose) { + Log.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities=" + + activities + ")"); + } + // TODO(b/122595322): implement + // TODO(b/119613670): log metrics + } + + @Override + public void setActivityContentCaptureEnabled(ComponentName activity, boolean enabled) { + if (mMaster.verbose) { + Log.v(TAG, "setActivityContentCaptureEnabled(activity=" + activity + ", enabled=" + + enabled + ")"); + } + // TODO(b/122595322): implement + // TODO(b/119613670): log metrics + } + + @Override + public void setPackageContentCaptureEnabled(String packageName, boolean enabled) { + if (mMaster.verbose) { + Log.v(TAG, + "setPackageContentCaptureEnabled(packageName=" + packageName + ", enabled=" + + enabled + ")"); + } + // TODO(b/122595322): implement + // TODO(b/119613670): log metrics + } + + @Override + public void getContentCaptureDisabledActivities(IResultReceiver receiver) { + // TODO(b/122595322): implement + } + + @Override + public void getContentCaptureDisabledPackages(IResultReceiver receiver) { + // TODO(b/122595322): implement + } + } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index ebe0083b398e..3c52e17ce1e8 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -83,6 +83,8 @@ final class ContentCaptureServerSession { */ @GuardedBy("mLock") public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) { + mService.getMaster().logRequestLocked("snapshot: id=" + mId); + mRemoteService.onActivitySnapshotRequest(mId, snapshotData); } diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 824162868e19..12742ca0a46f 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.os.IBinder; import android.service.contentcapture.IContentCaptureService; +import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.text.format.DateUtils; import android.util.Slog; @@ -35,12 +36,15 @@ final class RemoteContentCaptureService private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; + private final IBinder mServerCallback; + RemoteContentCaptureService(Context context, String serviceInterface, - ComponentName componentName, int userId, + ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId, ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed, boolean verbose) { - super(context, serviceInterface, componentName, userId, callbacks, + super(context, serviceInterface, serviceComponentName, userId, callbacks, bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2); + mServerCallback = callback.asBinder(); // Bind right away, which will trigger a onConnected() on service's scheduleBind(); @@ -69,7 +73,11 @@ final class RemoteContentCaptureService scheduleUnbind(); } try { - mService.onConnectedStateChanged(state); + if (state) { + mService.onConnected(mServerCallback); + } else { + mService.onDisconnected(); + } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d0666b98c0e0..d6f3e2ba4835 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4879,7 +4879,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, - mContext, mTrackerHandler, new NetworkMisc(networkMisc), this); + mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mNMS); // Make sure the network capabilities reflect what the agent info says. nai.networkCapabilities = mixInCapabilities(nai, nc); final String extraInfo = networkInfo.getExtraInfo(); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 869d564b4329..0b4c01ef90bd 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -257,6 +257,9 @@ public class LocationManagerService extends ILocationManager.Stub { packageManagerInternal.setLocationPackagesProvider( userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationProviderPackageNames)); + packageManagerInternal.setLocationExtraPackagesProvider( + userId -> mContext.getResources().getStringArray( + com.android.internal.R.array.config_locationExtraPackageNames)); // most startup is deferred until systemRunning() } @@ -1041,12 +1044,13 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onSetProperties(ProviderProperties properties) { - // move calls coming from below LMS onto a different thread to avoid deadlock - runInternal(() -> { - synchronized (mLock) { - mProperties = properties; - } - }); + // because this does not invoke any other methods which might result in calling back + // into the location provider, it is safe to run this on the calling thread. it is also + // currently necessary to run this on the calling thread to ensure that property changes + // are publicly visibly immediately, ie for mock providers which are created. + synchronized (mLock) { + mProperties = properties; + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index b0ca2df20f1f..aed0684a92cb 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -17,13 +17,11 @@ package com.android.server; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.Manifest.permission.DUMP; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; @@ -40,6 +38,7 @@ import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_TETHERING; + import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult; import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult; import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult; @@ -53,11 +52,9 @@ import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENAB import android.annotation.NonNull; import android.app.ActivityManager; -import android.content.ContentResolver; import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; -import android.net.TetherStatsParcel; import android.net.INetworkManagementEventObserver; import android.net.ITetheringStatsProvider; import android.net.InterfaceConfiguration; @@ -69,18 +66,15 @@ import android.net.NetworkPolicyManager; import android.net.NetworkStats; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.TetherStatsParcel; import android.net.UidRange; -import android.net.UidRangeParcel; import android.net.util.NetdService; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.INetworkActivityListener; import android.os.INetworkManagementService; -import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -91,12 +85,7 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; -import android.provider.Settings; import android.telephony.DataConnectionRealTimeInfo; -import android.telephony.PhoneStateListener; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; @@ -110,13 +99,11 @@ import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.DumpUtils; import com.android.internal.util.HexDump; import com.android.internal.util.Preconditions; -import com.android.server.NativeDaemonConnector.Command; -import com.android.server.NativeDaemonConnector.SensitiveArg; + import com.google.android.collect.Maps; import java.io.BufferedReader; import java.io.DataInputStream; -import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; @@ -124,15 +111,11 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; /** @@ -2153,28 +2136,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void startClatd(String interfaceName) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - - try { - mNetdService.clatdStart(interfaceName); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override - public void stopClatd(String interfaceName) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - - try { - mNetdService.clatdStop(interfaceName); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override public void registerNetworkActivityListener(INetworkActivityListener listener) { mNetworkActivityListeners.register(listener); } diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index 93e1dd36797a..16b12f1f1d68 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -7,7 +7,7 @@ "include-annotation": "android.platform.test.annotations.Presubmit" }, { - "exclude-annotation": "android.support.test.filters.FlakyTest" + "exclude-annotation": "androidx.test.filters.FlakyTest" } ] } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 1c04a94be7df..dd2b33ab1179 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -70,6 +70,12 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; static final String KEY_USE_COMPACTION = "use_compaction"; + static final String KEY_COMPACT_ACTION_1 = "compact_action_1"; + static final String KEY_COMPACT_ACTION_2 = "compact_action_2"; + static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1"; + static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; + static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; + static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; @@ -101,6 +107,12 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; private static final boolean DEFAULT_USE_COMPACTION = false; + public static final int DEFAULT_COMPACT_ACTION_1 = 1; + public static final int DEFAULT_COMPACT_ACTION_2 = 3; + public static final long DEFAULT_COMPACT_THROTTLE_1 = 5000; + public static final long DEFAULT_COMPACT_THROTTLE_2 = 10000; + public static final long DEFAULT_COMPACT_THROTTLE_3 = 500; + public static final long DEFAULT_COMPACT_THROTTLE_4 = 10000; // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -223,6 +235,20 @@ final class ActivityManagerConstants extends ContentObserver { // Use compaction for background apps. public boolean USE_COMPACTION = DEFAULT_USE_COMPACTION; + // Action for compactAppSome. + public int COMPACT_ACTION_1 = DEFAULT_COMPACT_ACTION_1; + // Action for compactAppFull; + public int COMPACT_ACTION_2 = DEFAULT_COMPACT_ACTION_2; + + // How long we'll skip second compactAppSome after first compactAppSome + public long COMPACT_THROTTLE_1 = DEFAULT_COMPACT_THROTTLE_1; + // How long we'll skip compactAppSome after compactAppFull + public long COMPACT_THROTTLE_2 = DEFAULT_COMPACT_THROTTLE_2; + // How long we'll skip compactAppFull after compactAppSome + public long COMPACT_THROTTLE_3 = DEFAULT_COMPACT_THROTTLE_3; + // How long we'll skip second compactAppFull after first compactAppFull + public long COMPACT_THROTTLE_4 = DEFAULT_COMPACT_THROTTLE_4; + // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; @@ -381,6 +407,12 @@ final class ActivityManagerConstants extends ContentObserver { TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION, DEFAULT_TOP_TO_FGS_GRACE_DURATION); USE_COMPACTION = mParser.getBoolean(KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION); + COMPACT_ACTION_1 = mParser.getInt(KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1); + COMPACT_ACTION_2 = mParser.getInt(KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2); + COMPACT_THROTTLE_1 = mParser.getLong(KEY_COMPACT_THROTTLE_1, DEFAULT_COMPACT_THROTTLE_1); + COMPACT_THROTTLE_2 = mParser.getLong(KEY_COMPACT_THROTTLE_2, DEFAULT_COMPACT_THROTTLE_2); + COMPACT_THROTTLE_3 = mParser.getLong(KEY_COMPACT_THROTTLE_3, DEFAULT_COMPACT_THROTTLE_3); + COMPACT_THROTTLE_4 = mParser.getLong(KEY_COMPACT_THROTTLE_4, DEFAULT_COMPACT_THROTTLE_4); updateMaxCachedProcesses(); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bd6fa498934c..089847d1ff7f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -32,6 +32,7 @@ import static android.app.AppOpsManager.OP_NONE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; import static android.content.pm.PackageManager.GET_PROVIDERS; +import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; @@ -318,7 +319,6 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.TriFunction; import com.android.server.AlarmManagerInternal; -import com.android.server.appop.AppOpsService; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; import com.android.server.DisplayThread; @@ -337,6 +337,7 @@ import com.android.server.ThreadPriorityBooster; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; import com.android.server.am.MemoryStatUtil.MemoryStat; +import com.android.server.appop.AppOpsService; import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; @@ -658,9 +659,47 @@ public class ActivityManagerService extends IActivityManager.Stub /** * When an app has restrictions on the other apps that can have associations with it, - * it appears here with a set of the allowed apps. + * it appears here with a set of the allowed apps and also track debuggability of the app. */ - ArrayMap<String, ArraySet<String>> mAllowedAssociations; + ArrayMap<String, PackageAssociationInfo> mAllowedAssociations; + + /** + * Tracks association information for a particular package along with debuggability. + * <p> Associations for a package A are allowed to package B if B is part of the + * allowed associations for A or if A is debuggable. + */ + private final class PackageAssociationInfo { + private final String mSourcePackage; + private final ArraySet<String> mAllowedPackageAssociations; + private boolean mIsDebuggable; + + PackageAssociationInfo(String sourcePackage, ArraySet<String> allowedPackages, + boolean isDebuggable) { + mSourcePackage = sourcePackage; + mAllowedPackageAssociations = allowedPackages; + mIsDebuggable = isDebuggable; + } + + /** + * Returns true if {@code mSourcePackage} is allowed association with + * {@code targetPackage}. + */ + boolean isPackageAssociationAllowed(String targetPackage) { + return mIsDebuggable || mAllowedPackageAssociations.contains(targetPackage); + } + + boolean isDebuggable() { + return mIsDebuggable; + } + + void setDebuggable(boolean isDebuggable) { + mIsDebuggable = isDebuggable; + } + + ArraySet<String> getAllowedPackageAssociations() { + return mAllowedPackageAssociations; + } + } /** * All of the processes we currently have running organized by pid. @@ -2392,12 +2431,10 @@ public class ActivityManagerService extends IActivityManager.Stub * If it does not, give it an empty set. */ void requireAllowedAssociationsLocked(String packageName) { - if (mAllowedAssociations == null) { - mAllowedAssociations = new ArrayMap<>( - SystemConfig.getInstance().getAllowedAssociations()); - } + ensureAllowedAssociations(); if (mAllowedAssociations.get(packageName) == null) { - mAllowedAssociations.put(packageName, new ArraySet<>()); + mAllowedAssociations.put(packageName, new PackageAssociationInfo(packageName, + new ArraySet<>(), /* isDebuggable = */ false)); } } @@ -2408,10 +2445,7 @@ public class ActivityManagerService extends IActivityManager.Stub * association is implicitly allowed. */ boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) { - if (mAllowedAssociations == null) { - mAllowedAssociations = new ArrayMap<>( - SystemConfig.getInstance().getAllowedAssociations()); - } + ensureAllowedAssociations(); // Interactions with the system uid are always allowed, since that is the core system // that everyone needs to be able to interact with. Also allow reflexive associations // within the same uid. @@ -2419,24 +2453,57 @@ public class ActivityManagerService extends IActivityManager.Stub || UserHandle.getAppId(uid2) == SYSTEM_UID) { return true; } - // We won't allow this association if either pkg1 or pkg2 has a limit on the - // associations that are allowed with it, and the other package is not explicitly - // specified as one of those associations. - ArraySet<String> pkgs = mAllowedAssociations.get(pkg1); - if (pkgs != null) { - if (!pkgs.contains(pkg2)) { - return false; - } + + // Check for association on both source and target packages. + PackageAssociationInfo pai = mAllowedAssociations.get(pkg1); + if (pai != null && !pai.isPackageAssociationAllowed(pkg2)) { + return false; } - pkgs = mAllowedAssociations.get(pkg2); - if (pkgs != null) { - return pkgs.contains(pkg1); + pai = mAllowedAssociations.get(pkg2); + if (pai != null && !pai.isPackageAssociationAllowed(pkg1)) { + return false; } // If no explicit associations are provided in the manifest, then assume the app is // allowed associations with any package. return true; } + /** Sets up allowed associations for system prebuilt packages from system config (if needed). */ + private void ensureAllowedAssociations() { + if (mAllowedAssociations == null) { + ArrayMap<String, ArraySet<String>> allowedAssociations = + SystemConfig.getInstance().getAllowedAssociations(); + mAllowedAssociations = new ArrayMap<>(allowedAssociations.size()); + PackageManagerInternal pm = getPackageManagerInternalLocked(); + for (int i = 0; i < allowedAssociations.size(); i++) { + final String pkg = allowedAssociations.keyAt(i); + final ArraySet<String> asc = allowedAssociations.valueAt(i); + + // Query latest debuggable flag from package-manager. + boolean isDebuggable = false; + try { + ApplicationInfo ai = AppGlobals.getPackageManager() + .getApplicationInfo(pkg, MATCH_ALL, 0); + if (ai != null) { + isDebuggable = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + } + } catch (RemoteException e) { + /* ignore */ + } + mAllowedAssociations.put(pkg, new PackageAssociationInfo(pkg, asc, isDebuggable)); + } + } + } + + /** Updates allowed associations for app info (specifically, based on debuggability). */ + private void updateAssociationForApp(ApplicationInfo appInfo) { + ensureAllowedAssociations(); + PackageAssociationInfo pai = mAllowedAssociations.get(appInfo.packageName); + if (pai != null) { + pai.setDebuggable((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + } + } + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -10628,7 +10695,6 @@ public class ActivityManagerService extends IActivityManager.Stub ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now); proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now)); } - } void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) { @@ -10910,14 +10976,14 @@ public class ActivityManagerService extends IActivityManager.Stub void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; - boolean printedAnything = false; pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)"); boolean printed = false; if (mAllowedAssociations != null) { for (int i = 0; i < mAllowedAssociations.size(); i++) { final String pkg = mAllowedAssociations.keyAt(i); - final ArraySet<String> asc = mAllowedAssociations.valueAt(i); + final ArraySet<String> asc = + mAllowedAssociations.valueAt(i).getAllowedPackageAssociations(); boolean printedHeader = false; for (int j = 0; j < asc.size(); j++) { if (dumpPackage == null || pkg.equals(dumpPackage) @@ -10926,7 +10992,6 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" Allowed associations (by restricted package):"); printed = true; needSep = true; - printedAnything = true; } if (!printedHeader) { pw.print(" * "); @@ -10938,6 +11003,9 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(asc.valueAt(j)); } } + if (mAllowedAssociations.valueAt(i).isDebuggable()) { + pw.println(" (debuggable)"); + } } } if (!printed) { @@ -14519,6 +14587,7 @@ public class ActivityManagerService extends IActivityManager.Stub + " ssp=" + ssp + " data=" + data); return ActivityManager.BROADCAST_SUCCESS; } + updateAssociationForApp(aInfo); mAtmInternal.onPackageReplaced(aInfo); mServices.updateServiceApplicationInfoLocked(aInfo); sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED, @@ -17798,6 +17867,15 @@ public class ActivityManagerService extends IActivityManager.Stub public void clearPendingBackup(int userId) { ActivityManagerService.this.clearPendingBackup(userId); } + + /** + * When power button is very long pressed, call this interface to do some pre-shutdown work + * like persisting database etc. + */ + @Override + public void prepareForPossibleShutdown() { + ActivityManagerService.this.prepareForPossibleShutdown(); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { @@ -18054,6 +18132,18 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * When power button is very long pressed, call this interface to do some pre-shutdown work + * like persisting database etc. + */ + public void prepareForPossibleShutdown() { + synchronized (this) { + if (mUsageStatsService != null) { + mUsageStatsService.prepareForPossibleShutdown(); + } + } + } + @VisibleForTesting public static class Injector { private NetworkManagementInternal mNmi; diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index aee16c3b9ddc..fe27c495ef55 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -44,7 +44,11 @@ public final class AppCompactor { */ final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>(); - /* + static final int COMPACT_PROCESS_SOME = 1; + static final int COMPACT_PROCESS_FULL = 2; + static final int COMPACT_PROCESS_MSG = 1; + + /** * This thread must be moved to the system background cpuset. * If that doesn't happen, it's probably going to draw a lot of power. * However, this has to happen after the first updateOomAdjLocked, because @@ -53,13 +57,22 @@ public final class AppCompactor { */ final ServiceThread mCompactionThread; - static final int COMPACT_PROCESS_SOME = 1; - static final int COMPACT_PROCESS_FULL = 2; - static final int COMPACT_PROCESS_MSG = 1; - final Handler mCompactionHandler; + final private Handler mCompactionHandler; - final ActivityManagerService mAm; - final ActivityManagerConstants mConstants; + final private ActivityManagerService mAm; + final private ActivityManagerConstants mConstants; + + final private String COMPACT_ACTION_FILE = "file"; + final private String COMPACT_ACTION_ANON = "anon"; + final private String COMPACT_ACTION_FULL = "full"; + + final private String compactActionSome; + final private String compactActionFull; + + final private long throttleSomeSome; + final private long throttleSomeFull; + final private long throttleFullSome; + final private long throttleFullFull; public AppCompactor(ActivityManagerService am) { mAm = am; @@ -69,6 +82,41 @@ public final class AppCompactor { THREAD_PRIORITY_FOREGROUND, true); mCompactionThread.start(); mCompactionHandler = new MemCompactionHandler(this); + + switch(mConstants.COMPACT_ACTION_1) { + case 1: + compactActionSome = COMPACT_ACTION_FILE; + break; + case 2: + compactActionSome = COMPACT_ACTION_ANON; + break; + case 3: + compactActionSome = COMPACT_ACTION_FULL; + break; + default: + compactActionSome = COMPACT_ACTION_FILE; + break; + } + + switch(mConstants.COMPACT_ACTION_2) { + case 1: + compactActionFull = COMPACT_ACTION_FILE; + break; + case 2: + compactActionFull = COMPACT_ACTION_ANON; + break; + case 3: + compactActionFull = COMPACT_ACTION_FULL; + break; + default: + compactActionFull = COMPACT_ACTION_FULL; + break; + } + + throttleSomeSome = mConstants.COMPACT_THROTTLE_1; + throttleSomeFull = mConstants.COMPACT_THROTTLE_2; + throttleFullSome = mConstants.COMPACT_THROTTLE_3; + throttleFullFull = mConstants.COMPACT_THROTTLE_4; } // Must be called while holding AMS lock. @@ -128,18 +176,16 @@ public final class AppCompactor { } // basic throttling + // use the ActivityManagerConstants knobs to determine whether current/prevous + // compaction combo should be throtted or not if (pendingAction == COMPACT_PROCESS_SOME) { - // if we're compacting some, then compact if >10s after last full - // or >5s after last some - if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) || - (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) { + if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleSomeSome)) || + (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleSomeFull))) { return; } } else { - // if we're compacting full, then compact if >10s after last full - // or >.5s after last some - if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) || - (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) { + if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleFullSome)) || + (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleFullFull))) { return; } } @@ -151,9 +197,9 @@ public final class AppCompactor { long[] rssBefore = Process.getRss(pid); FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); if (pendingAction == COMPACT_PROCESS_SOME) { - action = "file"; + action = compactActionSome; } else { - action = "all"; + action = compactActionFull; } fos.write(action.getBytes()); fos.close(); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index d7cb2bd36bde..d3953b58296c 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -67,9 +67,10 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class); sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class); + sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_ALL_APPS, int.class); sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACKLIST, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d704a3ea8eb1..9a4ca1f954d0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7824,8 +7824,10 @@ public class AudioService extends IAudioService.Stub /** see AudioPolicy.setUidDeviceAffinity() */ public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid, - @NonNull int[] deviceTypes, - @NonNull String[] deviceAddresses) { + @NonNull int[] deviceTypes, @NonNull String[] deviceAddresses) { + if (DEBUG_AP) { + Log.d(TAG, "setUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid); + } synchronized (mAudioPolicies) { final AudioPolicyProxy app = checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy"); @@ -7835,21 +7837,23 @@ public class AudioService extends IAudioService.Stub if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) { return AudioManager.ERROR; } + return app.setUidDeviceAffinities(uid, deviceTypes, deviceAddresses); } - return AudioManager.SUCCESS; } /** see AudioPolicy.removeUidDeviceAffinity() */ public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) { + if (DEBUG_AP) { + Log.d(TAG, "removeUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid); + } synchronized (mAudioPolicies) { final AudioPolicyProxy app = checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy"); if (app == null) { return AudioManager.ERROR; } - + return app.removeUidDeviceAffinities(uid); } - return AudioManager.SUCCESS; } public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) { @@ -8160,27 +8164,41 @@ public class AudioService extends IAudioService.Stub Binder.restoreCallingIdentity(identity); } - void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) { + int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) { final Integer Uid = new Integer(uid); + int res; if (mUidDeviceAffinities.remove(Uid) != null) { final long identity = Binder.clearCallingIdentity(); - AudioSystem.removeUidDeviceAffinities(uid); + res = AudioSystem.removeUidDeviceAffinities(uid); Binder.restoreCallingIdentity(identity); + if (res != AudioSystem.SUCCESS) { + Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, " + + " cannot call AudioSystem.setUidDeviceAffinities"); + return AudioManager.ERROR; + } } final long identity = Binder.clearCallingIdentity(); - final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses); + res = AudioSystem.setUidDeviceAffinities(uid, types, addresses); Binder.restoreCallingIdentity(identity); if (res == AudioSystem.SUCCESS) { mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses)); + return AudioManager.SUCCESS; } + Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed"); + return AudioManager.ERROR; } - void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) { + int removeUidDeviceAffinities(int uid) { if (mUidDeviceAffinities.remove(new Integer(uid)) != null) { final long identity = Binder.clearCallingIdentity(); - AudioSystem.removeUidDeviceAffinities(uid); + final int res = AudioSystem.removeUidDeviceAffinities(uid); Binder.restoreCallingIdentity(identity); + if (res == AudioSystem.SUCCESS) { + return AudioManager.SUCCESS; + } } + Log.e(TAG, "AudioSystem. removeUidDeviceAffinities failed"); + return AudioManager.ERROR; } }; diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index ecc3d2da8226..174ecfa12ee6 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -116,6 +116,7 @@ public abstract class BiometricServiceBase extends SystemService protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>(); // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap. protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>(); + protected int mHALDeathCount; protected class PerformanceStats { public int accept; // number of accepted biometrics @@ -596,6 +597,7 @@ public abstract class BiometricServiceBase extends SystemService public void serviceDied(long cookie) { Slog.e(getTag(), "HAL died"); mMetricsLogger.count(getMetrics().tagHalDied(), 1); + mHALDeathCount++; handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /*vendorCode */); } 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 5a9c1aca081a..a2aacdde4d9f 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -675,6 +675,12 @@ public class FaceService extends BiometricServiceBase { } @Override + public void serviceDied(long cookie) { + super.serviceDied(cookie); + mDaemon = null; + } + + @Override protected void updateActiveGroup(int userId, String clientPackage) { IBiometricsFace daemon = getFaceDaemon(); @@ -864,6 +870,8 @@ public class FaceService extends BiometricServiceBase { Slog.e(TAG, "dump formatting failure", e); } pw.println(dump); + pw.println("HAL Deaths: " + mHALDeathCount); + mHALDeathCount = 0; } private void dumpProto(FileDescriptor fd) { 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 1613dc97225b..3db6a74a1c6c 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -777,6 +777,12 @@ public class FingerprintService extends BiometricServiceBase { } @Override + public void serviceDied(long cookie) { + super.serviceDied(cookie); + mDaemon = null; + } + + @Override protected void updateActiveGroup(int userId, String clientPackage) { IBiometricsFingerprint daemon = getFingerprintDaemon(); @@ -1074,6 +1080,8 @@ public class FingerprintService extends BiometricServiceBase { Slog.e(TAG, "dump formatting failure", e); } pw.println(dump); + pw.println("HAL Deaths: " + mHALDeathCount); + mHALDeathCount = 0; } private void dumpProto(FileDescriptor fd) { diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 6596d27d021e..9d9b1cfdf6e2 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -16,8 +16,9 @@ package com.android.server.connectivity; -import android.net.InterfaceConfiguration; import android.net.ConnectivityManager; +import android.net.INetd; +import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -59,6 +60,7 @@ public class Nat464Xlat extends BaseNetworkObserver { NetworkInfo.State.SUSPENDED, }; + private final INetd mNetd; private final INetworkManagementService mNMService; // The network we're running on, and its type. @@ -76,7 +78,8 @@ public class Nat464Xlat extends BaseNetworkObserver { private String mIface; private State mState = State.IDLE; - public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) { + public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) { + mNetd = netd; mNMService = nmService; mNetwork = nai; } @@ -140,7 +143,7 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } try { - mNMService.startClatd(baseIface); + mNetd.clatdStart(baseIface); } catch(RemoteException|IllegalStateException e) { Slog.e(TAG, "Error starting clatd on " + baseIface, e); } @@ -162,7 +165,7 @@ public class Nat464Xlat extends BaseNetworkObserver { */ private void enterStoppingState() { try { - mNMService.stopClatd(mBaseIface); + mNetd.clatdStop(mBaseIface); } catch(RemoteException|IllegalStateException e) { Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e); } @@ -204,7 +207,7 @@ public class Nat464Xlat extends BaseNetworkObserver { Slog.e(TAG, "startClat: Can't start clat on null interface"); return; } - // TODO: should we only do this if mNMService.startClatd() succeeds? + // TODO: should we only do this if mNetd.clatdStart() succeeds? Slog.i(TAG, "Starting clatd on " + baseIface); enterStartingState(baseIface); } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 54c89aa04111..9ea73fbb1882 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import android.content.Context; +import android.net.INetd; import android.net.INetworkMonitor; import android.net.LinkProperties; import android.net.Network; @@ -239,12 +240,15 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private static final String TAG = ConnectivityService.class.getSimpleName(); private static final boolean VDBG = false; private final ConnectivityService mConnService; + private final INetd mNetd; + private final INetworkManagementService mNMS; private final Context mContext; private final Handler mHandler; public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, - NetworkMisc misc, ConnectivityService connService) { + NetworkMisc misc, ConnectivityService connService, INetd netd, + INetworkManagementService nms) { this.messenger = messenger; asyncChannel = ac; network = net; @@ -253,6 +257,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { networkCapabilities = nc; currentScore = score; mConnService = connService; + mNetd = netd; + mNMS = nms; mContext = context; mHandler = handler; networkMisc = misc; @@ -587,18 +593,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public void updateClat(INetworkManagementService netd) { if (Nat464Xlat.requiresClat(this)) { - maybeStartClat(netd); + maybeStartClat(); } else { maybeStopClat(); } } /** Ensure clat has started for this network. */ - public void maybeStartClat(INetworkManagementService netd) { + public void maybeStartClat() { if (clatd != null && clatd.isStarted()) { return; } - clatd = new Nat464Xlat(netd, this); + clatd = new Nat464Xlat(this, mNetd, mNMS); clatd.start(); } diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 3ea9810f2d76..97896889f243 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -282,6 +282,7 @@ public class PacManager { private void setCurrentProxyScript(String script) { if (mProxyService == null) { Log.e(TAG, "setCurrentProxyScript: no proxy service"); + return; } try { mProxyService.setPacFile(script); diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java index bfcc629541f8..aaf9cbc168af 100644 --- a/services/core/java/com/android/server/content/SyncJobService.java +++ b/services/core/java/com/android/server/content/SyncJobService.java @@ -141,10 +141,7 @@ public class SyncJobService extends JobService { final long runtime = nowUptime - startUptime; - if (startUptime == 0) { - wtf("Job " + jobId + " start uptime not found: " - + " params=" + jobParametersToString(params)); - } else if (runtime > 60 * 1000) { + if (runtime > 60 * 1000) { // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon. // (1 minute threshold.) // Also don't wtf when it's not ready to sync. diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java index 5cbe5b958b7d..fc20ef20f63d 100644 --- a/services/core/java/com/android/server/content/SyncLogger.java +++ b/services/core/java/com/android/server/content/SyncLogger.java @@ -16,6 +16,7 @@ package com.android.server.content; +import android.accounts.Account; import android.app.job.JobParameters; import android.os.Build; import android.os.Environment; @@ -31,6 +32,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IntPair; import com.android.server.IoThread; +import com.android.server.content.SyncManager.ActiveSyncContext; +import com.android.server.content.SyncStorageEngine.EndPoint; import libcore.io.IoUtils; @@ -309,4 +312,20 @@ public class SyncLogger { } } } + + static String logSafe(Account account) { + return account == null ? "[null]" : account.toSafeString(); + } + + static String logSafe(EndPoint endPoint) { + return endPoint == null ? "[null]" : endPoint.toSafeString(); + } + + static String logSafe(SyncOperation operation) { + return operation == null ? "[null]" : operation.toSafeString(); + } + + static String logSafe(ActiveSyncContext asc) { + return asc == null ? "[null]" : asc.toSafeString(); + } } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 8b93e041fa4c..7096477ce5f4 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -16,6 +16,8 @@ package com.android.server.content; +import static com.android.server.content.SyncLogger.logSafe; + import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; @@ -1140,7 +1142,7 @@ public class SyncManager { /* ignore - local call */ } if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) { - Log.w(TAG, "Access to " + account + " denied for package " + Log.w(TAG, "Access to " + logSafe(account) + " denied for package " + owningPackage + " in UID " + syncAdapterInfo.uid); return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS; } @@ -1474,7 +1476,8 @@ public class SyncManager { if (!syncOperation.ignoreBackoff()) { Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target); if (backoff == null) { - Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target); + Slog.e(TAG, "Couldn't find backoff values for " + + logSafe(syncOperation.target)); backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); } @@ -1745,8 +1748,8 @@ public class SyncManager { scheduleSyncOperationH(operation); } else { // Otherwise do not reschedule. - Log.d(TAG, "not retrying sync operation because the error is a hard error: " - + operation); + Log.e(TAG, "not retrying sync operation because the error is a hard error: " + + logSafe(operation)); } } @@ -1881,11 +1884,12 @@ public class SyncManager { sendSyncFinishedOrCanceledMessage(this, result); } - public void toString(StringBuilder sb) { + public void toString(StringBuilder sb, boolean logSafe) { sb.append("startTime ").append(mStartTime) .append(", mTimeoutStartTime ").append(mTimeoutStartTime) .append(", mHistoryRowId ").append(mHistoryRowId) - .append(", syncOperation ").append(mSyncOperation); + .append(", syncOperation ").append( + logSafe ? logSafe(mSyncOperation) : mSyncOperation); } public void onServiceConnected(ComponentName name, IBinder service) { @@ -1947,7 +1951,13 @@ public class SyncManager { public String toString() { StringBuilder sb = new StringBuilder(); - toString(sb); + toString(sb, false); + return sb.toString(); + } + + public String toSafeString() { + StringBuilder sb = new StringBuilder(); + toString(sb, true); return sb.toString(); } @@ -2036,7 +2046,7 @@ public class SyncManager { int count = 0; for (SyncOperation op: pendingSyncs) { if (!op.isPeriodic) { - pw.println(op.dump(null, false, buckets)); + pw.println(op.dump(null, false, buckets, /*logSafe=*/ false)); count++; } } @@ -2053,7 +2063,7 @@ public class SyncManager { int count = 0; for (SyncOperation op: pendingSyncs) { if (op.isPeriodic) { - pw.println(op.dump(null, false, buckets)); + pw.println(op.dump(null, false, buckets, /*logSafe=*/ false)); count++; } } @@ -2186,7 +2196,7 @@ public class SyncManager { sb.setLength(0); pw.print(formatDurationHMS(sb, durationInSeconds)); pw.print(" - "); - pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets)); + pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false)); pw.println(); } pw.println(); @@ -2974,7 +2984,7 @@ public class SyncManager { case SyncHandler.MESSAGE_CANCEL: SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj; Bundle extras = msg.peekData(); - if (Log.isLoggable(TAG, Log.DEBUG)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: " + endpoint + " bundle: " + extras); } @@ -2985,9 +2995,11 @@ public class SyncManager { SyncFinishedOrCancelledMessagePayload payload = (SyncFinishedOrCancelledMessagePayload) msg.obj; if (!isSyncStillActiveH(payload.activeSyncContext)) { - Log.d(TAG, "handleSyncHandlerMessage: dropping since the " - + "sync is no longer active: " - + payload.activeSyncContext); + if (isLoggable) { + Log.d(TAG, "handleSyncHandlerMessage: dropping since the " + + "sync is no longer active: " + + payload.activeSyncContext); + } break; } if (isLoggable) { @@ -3002,7 +3014,7 @@ public class SyncManager { case SyncHandler.MESSAGE_SERVICE_CONNECTED: { ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " + msgData.activeSyncContext); } @@ -3018,7 +3030,7 @@ public class SyncManager { case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { final ActiveSyncContext currentSyncContext = ((ServiceConnectionData) msg.obj).activeSyncContext; - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " + currentSyncContext); } @@ -3053,7 +3065,7 @@ public class SyncManager { case SyncHandler.MESSAGE_MONITOR_SYNC: ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj; - if (Log.isLoggable(TAG, Log.DEBUG)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " + monitoredSyncContext.mSyncOperation.target); } @@ -3061,7 +3073,7 @@ public class SyncManager { if (isSyncNotUsingNetworkH(monitoredSyncContext)) { Log.w(TAG, String.format( "Detected sync making no progress for %s. cancelling.", - monitoredSyncContext)); + logSafe(monitoredSyncContext))); SyncJobService.callJobFinished( monitoredSyncContext.mSyncOperation.jobId, false, "no network activity"); @@ -3558,7 +3570,8 @@ public class SyncManager { } catch (RuntimeException exc) { mLogger.log("Sync failed with RuntimeException: ", exc.toString()); closeActiveSyncContext(activeSyncContext); - Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); + Slog.e(TAG, "Caught RuntimeException while starting the sync " + + logSafe(syncOperation), exc); } } @@ -3658,7 +3671,8 @@ public class SyncManager { reschedulePeriodicSyncH(syncOperation); } } else { - Log.w(TAG, "failed sync operation " + syncOperation + ", " + syncResult); + Log.w(TAG, "failed sync operation " + + logSafe(syncOperation) + ", " + syncResult); syncOperation.retries++; if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) { @@ -4042,11 +4056,6 @@ public class SyncManager { getJobScheduler().cancel(op.jobId); } - private void wtfWithLog(String message) { - Slog.wtf(TAG, message); - mLogger.log("WTF: ", message); - } - public void resetTodayStats() { mSyncStorageEngine.resetTodayStats(/*force=*/ true); } diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 25edf4070689..2abc2e60a47b 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -363,14 +363,19 @@ public class SyncOperation { @Override public String toString() { - return dump(null, true, null); + return dump(null, true, null, false); } - String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) { + public String toSafeString() { + return dump(null, true, null, true); + } + + String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates, + boolean logSafe) { StringBuilder sb = new StringBuilder(); sb.append("JobId=").append(jobId) .append(" ") - .append(target.account.name) + .append(logSafe ? "***" : target.account.name) .append("/") .append(target.account.type) .append(" u") diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index bfd179141554..6b441a0d0f30 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -16,6 +16,8 @@ package com.android.server.content; +import static com.android.server.content.SyncLogger.logSafe; + import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; @@ -225,6 +227,15 @@ public class SyncStorageEngine { sb.append(":u" + userId); return sb.toString(); } + + public String toSafeString() { + StringBuilder sb = new StringBuilder(); + sb.append(account == null ? "ALL ACCS" : logSafe(account)) + .append("/") + .append(provider == null ? "ALL PDRS" : provider); + sb.append(":u" + userId); + return sb.toString(); + } } public static class AuthorityInfo { @@ -1861,8 +1872,8 @@ public class SyncStorageEngine { } } else { - Slog.w(TAG, "Failure adding authority: account=" - + accountName + " auth=" + authorityName + Slog.w(TAG, "Failure adding authority:" + + " auth=" + authorityName + " enabled=" + enabled + " syncable=" + syncable); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 2340b77c4a3c..cb3f91b7b6bb 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -792,6 +792,16 @@ public final class DisplayManagerService extends SystemService { } } + private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) { + synchronized (mSyncRoot) { + if (mVirtualDisplayAdapter == null) { + return; + } + + mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn); + } + } + private void registerDefaultDisplayAdapters() { // Register default display adapters. synchronized (mSyncRoot) { @@ -1930,6 +1940,16 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call + public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) { + final long token = Binder.clearCallingIdentity(); + try { + setVirtualDisplayStateInternal(callback.asBinder(), isOn); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 5aa585fcad75..1ca8dd3af4e2 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -147,6 +147,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter { return device; } + void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) { + VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); + if (device != null) { + device.setDisplayState(isOn); + } + } + /** * Returns the next unique index for the uniqueIdPrefix */ @@ -206,6 +213,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { private int mPendingChanges; private int mUniqueIndex; private Display.Mode mMode; + private boolean mIsDisplayOn; public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, int ownerUid, String ownerPackageName, @@ -226,6 +234,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mDisplayState = Display.STATE_UNKNOWN; mPendingChanges |= PENDING_SURFACE_CHANGE; mUniqueIndex = uniqueIndex; + mIsDisplayOn = surface != null; } @Override @@ -304,6 +313,14 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } } + void setDisplayState(boolean isOn) { + if (mIsDisplayOn != isOn) { + mIsDisplayOn = isOn; + mInfo = null; + sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); + } + } + public void stopLocked() { setSurfaceLocked(null); mStopped = true; @@ -375,7 +392,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mInfo.type = Display.TYPE_VIRTUAL; mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ? DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL; - mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF; + + mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF; + mInfo.ownerUid = mOwnerUid; mInfo.ownerPackageName = mOwnerPackageName; } diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java index 18d328d3f971..137833cc9551 100644 --- a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java @@ -51,6 +51,13 @@ public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction { } switch (cmd.getOpcode()) { case Constants.MESSAGE_FEATURE_ABORT: + if ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_INITIATE_ARC) { + audioSystem().setArcStatus(false); + finish(); + return true; + } else { + return false; + } case Constants.MESSAGE_REPORT_ARC_TERMINATED: audioSystem().setArcStatus(false); finish(); diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index ff029c194f12..297a418fa5e3 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; import android.annotation.IntDef; +import android.annotation.StringDef; import android.hardware.hdmi.HdmiDeviceInfo; import java.lang.annotation.Retention; @@ -222,6 +223,15 @@ final class Constants { static final int AUDIO_CODEC_WMAPRO = 0xE; // Support WMA-Pro static final int AUDIO_CODEC_MAX = 0xF; + @StringDef({ + AUDIO_DEVICE_ARC_IN, + AUDIO_DEVICE_SPDIF, + }) + public @interface AudioDevice {} + + static final String AUDIO_DEVICE_ARC_IN = "ARC_IN"; + static final String AUDIO_DEVICE_SPDIF = "SPDIF"; + // Bit mask used to get the routing path of the top level device. // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0. static final int ROUTING_PATH_TOP_MASK = 0xF000; diff --git a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java index 0495ff878ce0..71873198d798 100644 --- a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java +++ b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java @@ -50,8 +50,10 @@ public class DetectTvSystemAudioModeSupportAction extends HdmiCecFeatureAction { if (mState != STATE_WAITING_FOR_FEATURE_ABORT) { return false; } - finishAction(false); - return true; + if ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE) { + finishAction(false); + return true; + } } return false; } diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index ba21b7882afd..df0dc77613b1 100755 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -16,6 +16,7 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; @@ -51,6 +52,10 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private static final int STATE_WAITING_FOR_OSD_NAME = 3; // State in which the action is waiting for gathering vendor id of non-local devices. private static final int STATE_WAITING_FOR_VENDOR_ID = 4; + // State in which the action is waiting for devices to be ready. + private static final int STATE_WAITING_FOR_DEVICES = 5; + // State in which the action is waiting for gathering power status of non-local devices. + private static final int STATE_WAITING_FOR_POWER = 6; /** * Interface used to report result of device discovery. @@ -72,6 +77,7 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS; private int mPortId = Constants.INVALID_PORT_ID; private int mVendorId = Constants.UNKNOWN_VENDOR_ID; + private int mPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN; private String mDisplayName = ""; private int mDeviceType = HdmiDeviceInfo.DEVICE_INACTIVE; @@ -81,7 +87,7 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private HdmiDeviceInfo toHdmiDeviceInfo() { return new HdmiDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType, - mVendorId, mDisplayName); + mVendorId, mDisplayName, mPowerStatus); } } @@ -89,16 +95,28 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private final DeviceDiscoveryCallback mCallback; private int mProcessedDeviceCount = 0; private int mTimeoutRetry = 0; - private boolean mIsTvDevice = source().mService.isTvDevice(); + private boolean mIsTvDevice = localDevice().mService.isTvDevice(); + private final int mDelayPeriod; /** * Constructor. * * @param source an instance of {@link HdmiCecLocalDevice}. + * @param delay delay action for this period between query Physical Address and polling */ - DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) { + DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback, int delay) { super(source); mCallback = Preconditions.checkNotNull(callback); + mDelayPeriod = delay; + } + + /** + * Constructor. + * + * @param source an instance of {@link HdmiCecLocalDevice}. + */ + DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) { + this(source, callback, 0); } @Override @@ -117,7 +135,11 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { Slog.v(TAG, "Device detected: " + ackedAddress); allocateDevices(ackedAddress); - startPhysicalAddressStage(); + if (mDelayPeriod > 0) { + startToDelayAction(); + } else { + startPhysicalAddressStage(); + } } }, Constants.POLL_ITERATION_REVERSE_ORDER | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY); @@ -131,6 +153,13 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { } } + private void startToDelayAction() { + Slog.v(TAG, "Waiting for connected devices to be ready"); + mState = STATE_WAITING_FOR_DEVICES; + + checkAndProceedStage(); + } + private void startPhysicalAddressStage() { Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size()); mProcessedDeviceCount = 0; @@ -159,6 +188,11 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { addTimer(mState, HdmiConfig.TIMEOUT_MS); } + private void delayActionWithTimePeriod(int timeDelay) { + mActionTimer.clearTimerMessage(); + addTimer(mState, timeDelay); + } + private void startOsdNameStage() { Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size()); mProcessedDeviceCount = 0; @@ -207,6 +241,29 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { addTimer(mState, HdmiConfig.TIMEOUT_MS); } + private void startPowerStatusStage() { + Slog.v(TAG, "Start [Power Status Stage]:" + mDevices.size()); + mProcessedDeviceCount = 0; + mState = STATE_WAITING_FOR_POWER; + + checkAndProceedStage(); + } + + private void queryPowerStatus(int address) { + if (!verifyValidLogicalAddress(address)) { + checkAndProceedStage(); + return; + } + + mActionTimer.clearTimerMessage(); + + if (mayProcessMessageIfCached(address, Constants.MESSAGE_REPORT_POWER_STATUS)) { + return; + } + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address)); + addTimer(mState, HdmiConfig.TIMEOUT_MS); + } + private boolean mayProcessMessageIfCached(int address, int opcode) { HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode); if (message != null) { @@ -245,6 +302,16 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { return true; } return false; + case STATE_WAITING_FOR_POWER: + if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { + handleReportPowerStatus(cmd); + return true; + } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) + && ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_REPORT_POWER_STATUS)) { + handleReportPowerStatus(cmd); + return true; + } + return false; case STATE_WAITING_FOR_DEVICE_POLLING: // Fall through. default: @@ -266,6 +333,7 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params); current.mPortId = getPortId(current.mPhysicalAddress); current.mDeviceType = params[2] & 0xFF; + current.mDisplayName = HdmiUtils.getDefaultDeviceName(current.mDeviceType); // TODO(amyjojo): check if non-TV device needs to update cec switch info. // This is to manager CEC device separately in case they don't have address. @@ -329,6 +397,26 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { checkAndProceedStage(); } + private void handleReportPowerStatus(HdmiCecMessage cmd) { + Preconditions.checkState(mProcessedDeviceCount < mDevices.size()); + + DeviceInfo current = mDevices.get(mProcessedDeviceCount); + if (current.mLogicalAddress != cmd.getSource()) { + Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" + + cmd.getSource()); + return; + } + + if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) { + byte[] params = cmd.getParams(); + int powerStatus = params[0] & 0xFF; + current.mPowerStatus = powerStatus; + } + + increaseProcessedDeviceCount(); + checkAndProceedStage(); + } + private void increaseProcessedDeviceCount() { mProcessedDeviceCount++; mTimeoutRetry = 0; @@ -372,6 +460,9 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { startVendorIdStage(); return; case STATE_WAITING_FOR_VENDOR_ID: + startPowerStatusStage(); + return; + case STATE_WAITING_FOR_POWER: wrapUpAndFinish(); return; default: @@ -385,6 +476,9 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private void sendQueryCommand() { int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress; switch (mState) { + case STATE_WAITING_FOR_DEVICES: + delayActionWithTimePeriod(mDelayPeriod); + return; case STATE_WAITING_FOR_PHYSICAL_ADDRESS: queryPhysicalAddress(address); return; @@ -394,6 +488,9 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { case STATE_WAITING_FOR_VENDOR_ID: queryVendorId(address); return; + case STATE_WAITING_FOR_POWER: + queryPowerStatus(address); + return; default: return; } @@ -405,13 +502,24 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { return; } + if (mState == STATE_WAITING_FOR_DEVICES) { + startPhysicalAddressStage(); + return; + } if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) { sendQueryCommand(); return; } mTimeoutRetry = 0; Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount); - removeDevice(mProcessedDeviceCount); + if (mState != STATE_WAITING_FOR_POWER && mState != STATE_WAITING_FOR_OSD_NAME) { + // We don't need to remove the device info if the power status is unknown. + // Some device does not have preferred OSD name and does not respond to Give OSD name. + // Like LG TV. We can give it default device name and not remove it. + removeDevice(mProcessedDeviceCount); + } else { + increaseProcessedDeviceCount(); + } checkAndProceedStage(); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index e777ce8166ac..86be585e5d23 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -77,7 +77,7 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; - private static final int MAX_CEC_MESSAGE_HISTORY = 20; + private static final int MAX_CEC_MESSAGE_HISTORY = 200; // Predicate for whether the given logical address is remote device's one or not. private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() { @@ -682,7 +682,7 @@ final class HdmiCecController { void dump(final IndentingPrintWriter pw) { for (int i = 0; i < mLocalDevices.size(); ++i) { - pw.println("HdmiCecLocalDevice #" + i + ":"); + pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":"); pw.increaseIndent(); mLocalDevices.valueAt(i).dump(pw); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 32dc02613512..414f6bbfb995 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -56,16 +56,12 @@ abstract class HdmiCecLocalDevice { // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior. // When it expires, we can assume <User Control Release> is received. private static final int FOLLOWER_SAFETY_TIMEOUT = 550; - /** - * Return value of {@link #getLocalPortFromPhysicalAddress(int)} - */ - private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1; - private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0; protected final HdmiControlService mService; protected final int mDeviceType; protected int mAddress; protected int mPreferredAddress; + @GuardedBy("mLock") protected HdmiDeviceInfo mDeviceInfo; protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; protected int mLastKeyRepeatCount = 0; @@ -717,16 +713,18 @@ abstract class HdmiCecLocalDevice { return mDeviceType; } - @ServiceThreadOnly + @GuardedBy("mLock") HdmiDeviceInfo getDeviceInfo() { - assertRunOnServiceThread(); - return mDeviceInfo; + synchronized (mLock) { + return mDeviceInfo; + } } - @ServiceThreadOnly + @GuardedBy("mLock") void setDeviceInfo(HdmiDeviceInfo info) { - assertRunOnServiceThread(); - mDeviceInfo = info; + synchronized (mLock) { + mDeviceInfo = info; + } } // Returns true if the logical address is same as the argument. @@ -1058,47 +1056,6 @@ abstract class HdmiCecLocalDevice { pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath)); } - /** - * Method to parse target physical address to the port number on the current device. - * - * <p>This check assumes target address is valid. - * @param targetPhysicalAddress is the physical address of the target device - * @return - * If the target device is under the current device, return the port number of current device - * that the target device is connected to. - * - * <p>If the target device has the same physical address as the current device, return - * {@link #TARGET_SAME_PHYSICAL_ADDRESS}. - * - * <p>If the target device is not under the current device, return - * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}. - */ - protected int getLocalPortFromPhysicalAddress(int targetPhysicalAddress) { - int myPhysicalAddress = mService.getPhysicalAddress(); - if (myPhysicalAddress == targetPhysicalAddress) { - return TARGET_SAME_PHYSICAL_ADDRESS; - } - int finalMask = 0xF000; - int mask; - int port = 0; - for (mask = 0x0F00; mask > 0x000F; mask >>= 4) { - if ((myPhysicalAddress & mask) == 0) { - port = mask & targetPhysicalAddress; - break; - } else { - finalMask |= mask; - } - } - if (finalMask != 0xFFFF && (finalMask & targetPhysicalAddress) == myPhysicalAddress) { - while (mask != 0x000F) { - mask >>= 4; - port >>= 4; - } - return port; - } - return TARGET_NOT_UNDER_LOCAL_DEVICE; - } - /** Calculates the physical address for {@code activePortId}. * * <p>This method assumes current device physical address is valid. diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 0e4e3342451c..63214ed651b9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -26,22 +26,38 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.media.AudioDeviceInfo; +import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioSystem; import android.media.tv.TvContract; import android.os.SystemProperties; +import android.provider.Settings.Global; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.Constants.AudioCodec; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; +import com.android.server.hdmi.HdmiUtils.CodecSad; +import com.android.server.hdmi.HdmiUtils.DeviceConfig; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; + /** * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android @@ -77,25 +93,28 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { // processing. private final HashMap<Integer, String> mTvInputs = new HashMap<>(); + // Copy of mDeviceInfos to guarantee thread-safety. + @GuardedBy("mLock") + private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList(); + // Map-like container of all cec devices. // device id is used as key of container. private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>(); protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); - mSystemAudioControlFeatureEnabled = true; - // TODO(amyjojo) make System Audio Control controllable by users - /*mSystemAudioControlFeatureEnabled = - mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/ - // TODO(amyjojo): make the map ro property. - mTvInputs.put(Constants.CEC_SWITCH_HDMI1, - "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5"); - mTvInputs.put(Constants.CEC_SWITCH_HDMI2, - "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6"); - mTvInputs.put(Constants.CEC_SWITCH_HDMI3, - "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7"); + mRoutingControlFeatureEnabled = + mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false); + mSystemAudioControlFeatureEnabled = + mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true); + // TODO(amyjojo): Maintain a portId to TvinputId map. + mTvInputs.put(2, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5"); + mTvInputs.put(4, "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6"); + mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7"); } + private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml"; + /** * Called when a device is newly added or a new device is detected or * an existing device is updated. @@ -167,6 +186,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { removeDeviceInfo(deviceInfo.getId()); } mDeviceInfos.append(deviceInfo.getId(), deviceInfo); + updateSafeDeviceInfoList(); return oldDeviceInfo; } @@ -184,6 +204,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { if (deviceInfo != null) { mDeviceInfos.remove(id); } + updateSafeDeviceInfoList(); return deviceInfo; } @@ -200,6 +221,24 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress)); } + @ServiceThreadOnly + private void updateSafeDeviceInfoList() { + assertRunOnServiceThread(); + List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos); + synchronized (mLock) { + mSafeAllDeviceInfos = copiedDevices; + } + } + + @GuardedBy("mLock") + List<HdmiDeviceInfo> getSafeCecDevicesLocked() { + ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>(); + for (HdmiDeviceInfo info : mSafeAllDeviceInfos) { + infoList.add(info); + } + return infoList; + } + private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) { mService.invokeDeviceEventListeners(info, status); } @@ -229,7 +268,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mTvSystemAudioModeSupport = false; // Record the last state of System Audio Control before going to standby synchronized (mLock) { - SystemProperties.set( + mService.writeStringSetting( Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, mSystemAudioActivated ? "true" : "false"); } @@ -240,6 +279,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); + if (reason == mService.INITIATED_BY_ENABLE_CEC) { + mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(), + getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST); + } mService.sendCecCommand( HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); @@ -259,7 +302,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @Override protected int findKeyReceiverAddress() { - return Constants.ADDR_TV; + if (getActiveSource().isValid()) { + return getActiveSource().logicalAddress; + } + return Constants.ADDR_INVALID; } @VisibleForTesting @@ -267,7 +313,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) { if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON) || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON) - && lastSystemAudioControlStatus)) { + && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) { addAndStartAction(new SystemAudioInitiationActionFromAvr(this)); } } @@ -284,7 +330,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void setPreferredAddress(int addr) { assertRunOnServiceThread(); - SystemProperties.set( + mService.writeStringSetting( Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr)); } @@ -400,8 +446,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected boolean handleGiveAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); - - reportAudioStatus(message); + if (isSystemAudioControlFeatureEnabled()) { + reportAudioStatus(message.getSource()); + } else { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + } return true; } @@ -420,7 +469,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { protected boolean handleRequestArcInitiate(HdmiCecMessage message) { assertRunOnServiceThread(); removeAction(ArcInitiationActionFromAvr.class); - if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) { + if (!mService.readBooleanSetting(Constants.PROPERTY_ARC_SUPPORT, true)) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE); } else if (!isDirectConnectToTv()) { HdmiLogger.debug("AVR device is not directly connected with TV"); @@ -459,13 +508,35 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE); return true; } - AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo(); - if (deviceInfo == null) { - mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE); - return true; + + List<DeviceConfig> config = null; + File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH); + if (file.exists()) { + try { + InputStream in = new FileInputStream(file); + config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in); + in.close(); + } catch (IOException e) { + Slog.e(TAG, "Error reading file: " + file, e); + } catch (XmlPullParserException e) { + Slog.e(TAG, "Unable to parse file: " + file, e); + } } + @AudioCodec int[] audioFormatCodes = parseAudioFormatCodes(message.getParams()); - byte[] sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes); + byte[] sadBytes; + if (config != null && config.size() > 0) { + sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioFormatCodes); + } else { + AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo(); + if (deviceInfo == null) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE); + return true; + } + + sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes); + } + if (sadBytes.length == 0) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND); } else { @@ -478,14 +549,127 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { private byte[] getSupportedShortAudioDescriptors( AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) { - // TODO(b/80297701) implement - return new byte[] {}; + ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length); + for (@AudioCodec int audioFormatCode : audioFormatCodes) { + byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode); + if (sad != null) { + if (sad.length == 3) { + + sads.add(sad); + } else { + HdmiLogger.warning( + "Dropping Short Audio Descriptor with length %d for requested codec %x", + sad.length, audioFormatCode); + } + } + } + return getShortAudioDescriptorBytes(sads); + } + + private byte[] getSupportedShortAudioDescriptorsFromConfig( + List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) { + DeviceConfig deviceConfigToUse = null; + for (DeviceConfig device : deviceConfig) { + // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name + if (device.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) { + deviceConfigToUse = device; + break; + } + } + if (deviceConfigToUse == null) { + // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name + Slog.w(TAG, "sadConfig.xml does not have required device info for " + + "VX_AUDIO_DEVICE_IN_HDMI_ARC"); + return new byte[0]; + } + HashMap<Integer, byte[]> map = new HashMap<>(); + ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length); + for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) { + map.put(codecSad.audioCodec, codecSad.sad); + } + for (int i = 0; i < audioFormatCodes.length; i++) { + if (map.containsKey(audioFormatCodes[i])) { + byte[] sad = map.get(audioFormatCodes[i]); + if (sad != null && sad.length == 3) { + sads.add(sad); + } + } + } + return getShortAudioDescriptorBytes(sads); + } + + private byte[] getShortAudioDescriptorBytes(ArrayList<byte[]> sads) { + // Short Audio Descriptors are always 3 bytes long. + byte[] bytes = new byte[sads.size() * 3]; + int index = 0; + for (byte[] sad : sads) { + System.arraycopy(sad, 0, bytes, index, 3); + index += 3; + } + return bytes; + } + + /** + * Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the + * audioFormatCode is not supported. + */ + @Nullable + private byte[] getSupportedShortAudioDescriptor( + AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) { + switch (audioFormatCode) { + case Constants.AUDIO_CODEC_NONE: { + return null; + } + case Constants.AUDIO_CODEC_LPCM: { + return getLpcmShortAudioDescriptor(deviceInfo); + } + // TODO(b/80297701): implement the rest of the codecs + case Constants.AUDIO_CODEC_DD: + case Constants.AUDIO_CODEC_MPEG1: + case Constants.AUDIO_CODEC_MP3: + case Constants.AUDIO_CODEC_MPEG2: + case Constants.AUDIO_CODEC_AAC: + case Constants.AUDIO_CODEC_DTS: + case Constants.AUDIO_CODEC_ATRAC: + case Constants.AUDIO_CODEC_ONEBITAUDIO: + case Constants.AUDIO_CODEC_DDP: + case Constants.AUDIO_CODEC_DTSHD: + case Constants.AUDIO_CODEC_TRUEHD: + case Constants.AUDIO_CODEC_DST: + case Constants.AUDIO_CODEC_WMAPRO: + default: { + return null; + } + } + } + + @Nullable + private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) { + // TODO(b/80297701): implement + return null; } @Nullable private AudioDeviceInfo getSystemAudioDeviceInfo() { - // TODO(b/80297701) implement - // Get the audio device used for system audio mode. + AudioManager audioManager = mService.getContext().getSystemService(AudioManager.class); + if (audioManager == null) { + HdmiLogger.error( + "Error getting system audio device because AudioManager not available."); + return null; + } + AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS); + HdmiLogger.debug("Found %d audio input devices", devices.length); + for (AudioDeviceInfo device : devices) { + HdmiLogger.debug("%s at port %s", device.getProductName(), device.getPort()); + HdmiLogger.debug("Supported encodings are %s", + Arrays.stream(device.getEncodings()).mapToObj( + AudioFormat::toLogFriendlyEncoding + ).collect(Collectors.joining(", "))); + // TODO(b/80297701) use the actual device type that system audio mode is connected to. + if (device.getType() == AudioDeviceInfo.TYPE_HDMI_ARC) { + return device; + } + } return null; } @@ -583,17 +767,20 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", ""); } - private void reportAudioStatus(HdmiCecMessage message) { + void reportAudioStatus(int source) { assertRunOnServiceThread(); int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC); boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC); int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC); int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); + HdmiLogger.debug("Reporting volume %i (%i-%i) as CEC volume %i", volume, + minVolume, maxVolume, scaledVolume); mService.sendCecCommand( HdmiCecMessageBuilder.buildReportAudioStatus( - mAddress, message.getSource(), scaledVolume, mute)); + mAddress, source, scaledVolume, mute)); } /** @@ -633,7 +820,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { */ private void setSystemAudioMode(boolean newSystemAudioMode) { int targetPhysicalAddress = getActiveSource().physicalAddress; - int port = getLocalPortFromPhysicalAddress(targetPhysicalAddress); + int port = mService.pathToPortId(targetPhysicalAddress); if (newSystemAudioMode && port >= 0) { switchToAudioInput(); } @@ -641,16 +828,18 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { // PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE is false when device never needs to be muted. boolean currentMuteStatus = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC); - if (SystemProperties.getBoolean( - Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true) - && currentMuteStatus == newSystemAudioMode) { - mService.getAudioManager() - .adjustStreamVolume( - AudioManager.STREAM_MUSIC, - newSystemAudioMode - ? AudioManager.ADJUST_UNMUTE - : AudioManager.ADJUST_MUTE, - 0); + if (currentMuteStatus == newSystemAudioMode) { + if (mService.readBooleanSetting( + Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true) + || newSystemAudioMode) { + mService.getAudioManager() + .adjustStreamVolume( + AudioManager.STREAM_MUSIC, + newSystemAudioMode + ? AudioManager.ADJUST_UNMUTE + : AudioManager.ADJUST_MUTE, + 0); + } } updateAudioManagerForSystemAudio(newSystemAudioMode); synchronized (mLock) { @@ -688,6 +877,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device); } + void onSystemAduioControlFeatureSupportChanged(boolean enabled) { + setSystemAudioControlFeatureEnabled(enabled); + if (enabled) { + addAndStartAction(new SystemAudioInitiationActionFromAvr(this)); + } + } + @ServiceThreadOnly void setSystemAudioControlFeatureEnabled(boolean enabled) { assertRunOnServiceThread(); @@ -697,6 +893,14 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } @ServiceThreadOnly + void setRoutingControlFeatureEnables(boolean enabled) { + assertRunOnServiceThread(); + synchronized (mLock) { + mRoutingControlFeatureEnabled = enabled; + } + } + + @ServiceThreadOnly void doManualPortSwitching(int portId, IHdmiControlCallback callback) { assertRunOnServiceThread(); // TODO: validate port ID @@ -817,7 +1021,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @Override protected void switchInputOnReceivingNewActivePath(int physicalAddress) { - int port = getLocalPortFromPhysicalAddress(physicalAddress); + int port = mService.pathToPortId(physicalAddress); if (isSystemAudioActivated() && port < 0) { // If system audio mode is on and the new active source is not under the current device, // Will switch to ARC input. @@ -831,6 +1035,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } protected void routeToInputFromPortId(int portId) { + if (!isRoutingControlFeatureEnabled()) { + HdmiLogger.debug("Routing Control Feature is not enabled."); + return; + } if (mArcIntentUsed) { routeToTvInputFromPortId(portId); } else { @@ -885,7 +1093,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @Override protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) { - int port = getLocalPortFromPhysicalAddress(physicalAddress); + int port = mService.pathToPortId(physicalAddress); // Routing change or information sent from switches under the current device can be ignored. if (port > 0) { return; @@ -918,8 +1126,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { return; } - int routingInformationPath = - getActivePathOnSwitchFromActivePortId(getRoutingPort()); + int routingInformationPath = mService.portIdToPath(getRoutingPort()); // If current device is already the leaf of the whole HDMI system, will do nothing. if (routingInformationPath == mService.getPhysicalAddress()) { HdmiLogger.debug("Current device can't assign valid physical address" @@ -954,6 +1161,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly private void launchDeviceDiscovery() { assertRunOnServiceThread(); + if (hasAction(DeviceDiscoveryAction.class)) { + Slog.i(TAG, "Device Discovery Action is in progress. Restarting."); + removeAction(DeviceDiscoveryAction.class); + } DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, new DeviceDiscoveryCallback() { @Override @@ -977,5 +1188,25 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE); } mDeviceInfos.clear(); + updateSafeDeviceInfoList(); } + + @Override + protected void dump(IndentingPrintWriter pw) { + pw.println("HdmiCecLocalDeviceAudioSystem:"); + pw.increaseIndent(); + pw.println("mSystemAudioActivated: " + mSystemAudioActivated); + pw.println("isRoutingFeatureEnabled " + isRoutingControlFeatureEnabled()); + pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled); + pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport); + pw.println("mArcEstablished: " + mArcEstablished); + pw.println("mArcIntentUsed: " + mArcIntentUsed); + pw.println("mRoutingPort: " + getRoutingPort()); + pw.println("mLocalActivePort: " + getLocalActivePort()); + HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs); + HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos); + pw.decreaseIndent(); + super.dump(pw); + } + } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 07db9716497c..7a0c27906122 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -77,6 +77,10 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); + if (reason == mService.INITIATED_BY_ENABLE_CEC) { + mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(), + getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST); + } mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( @@ -96,7 +100,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void setPreferredAddress(int addr) { assertRunOnServiceThread(); - SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, + mService.writeStringSetting(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, String.valueOf(addr)); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index a95f7f1f3f3d..ae008b4bfa7a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -66,6 +66,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @LocalActivePort protected int mLocalActivePort = Constants.CEC_SWITCH_HOME; + // Whether the Routing Coutrol feature is enabled or not. False by default. + @GuardedBy("mLock") + protected boolean mRoutingControlFeatureEnabled; + protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) { super(service, deviceType); } @@ -123,7 +127,9 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } setIsActiveSource(physicalAddress == mService.getPhysicalAddress()); updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON); - switchInputOnReceivingNewActivePath(physicalAddress); + if (isRoutingControlFeatureEnabled()) { + switchInputOnReceivingNewActivePath(physicalAddress); + } return true; } @@ -153,6 +159,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRoutingChange(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!isRoutingControlFeatureEnabled()) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + return true; + } int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); // if the current device is a pure playback device if (!mIsSwitchDevice @@ -168,6 +178,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRoutingInformation(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!isRoutingControlFeatureEnabled()) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + return true; + } int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); // if the current device is a pure playback device if (!mIsSwitchDevice @@ -204,7 +218,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { // This method should only be called when the device can be the active source. protected void setAndBroadcastActiveSource(HdmiCecMessage message, int physicalAddress) { mService.setAndBroadcastActiveSource( - message, physicalAddress, getDeviceInfo().getDeviceType()); + physicalAddress, getDeviceInfo().getDeviceType(), message.getSource()); } @ServiceThreadOnly @@ -279,6 +293,12 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } } + boolean isRoutingControlFeatureEnabled() { + synchronized (mLock) { + return mRoutingControlFeatureEnabled; + } + } + // Check if the device is trying to switch to the same input that is active right now. // This can help avoid redundant port switching. protected boolean isSwitchingToTheSameInput(@LocalActivePort int activePort) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java index c0056158a9c5..f8b39627f236 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; import android.annotation.Nullable; + import libcore.util.EmptyArray; import java.util.Arrays; @@ -111,12 +112,11 @@ public final class HdmiCecMessage { @Override public String toString() { StringBuffer s = new StringBuffer(); - s.append(String.format("<%s> src: %d, dst: %d", - opcodeToString(mOpcode), mSource, mDestination)); + s.append(String.format("<%s> %X%X:%02X", + opcodeToString(mOpcode), mSource, mDestination, mOpcode)); if (mParams.length > 0) { - s.append(", params:"); for (byte data : mParams) { - s.append(String.format(" %02X", data)); + s.append(String.format(":%02X", data)); } } return s.toString(); @@ -133,7 +133,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_TUNER_STEP_DECREMENT: return "Tuner Step Decrement"; case Constants.MESSAGE_TUNER_DEVICE_STATUS: - return "Tuner Device Staus"; + return "Tuner Device Status"; case Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS: return "Give Tuner Device Status"; case Constants.MESSAGE_RECORD_ON: @@ -207,7 +207,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_DEVICE_VENDOR_ID: return "Device Vendor Id"; case Constants.MESSAGE_VENDOR_COMMAND: - return "Vendor Commandn"; + return "Vendor Command"; case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN: return "Vendor Remote Button Down"; case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP: @@ -215,7 +215,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID: return "Give Device Vendor Id"; case Constants.MESSAGE_MENU_REQUEST: - return "Menu REquest"; + return "Menu Request"; case Constants.MESSAGE_MENU_STATUS: return "Menu Status"; case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS: @@ -247,7 +247,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_SET_EXTERNAL_TIMER: return "Set External Timer"; case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR: - return "Repot Short Audio Descriptor"; + return "Report Short Audio Descriptor"; case Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR: return "Request Short Audio Descriptor"; case Constants.MESSAGE_INITIATE_ARC: diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2d6e76294e4a..46219d5cbe8a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -20,12 +20,14 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE; import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE; import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH; +import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; import static com.android.server.hdmi.Constants.DISABLED; import static com.android.server.hdmi.Constants.ENABLED; import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE; import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING; import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE; import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL; +import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY; import android.annotation.Nullable; import android.content.BroadcastReceiver; @@ -184,9 +186,10 @@ public class HdmiControlService extends SystemService { @Override public void onReceive(Context context, Intent intent) { assertRunOnServiceThread(); + boolean isReboot = SystemProperties.get(SHUTDOWN_ACTION_PROPERTY).contains("1"); switch (intent.getAction()) { case Intent.ACTION_SCREEN_OFF: - if (isPowerOnOrTransient()) { + if (isPowerOnOrTransient() && !isReboot) { onStandby(STANDBY_SCREEN_OFF); } break; @@ -202,7 +205,7 @@ public class HdmiControlService extends SystemService { } break; case Intent.ACTION_SHUTDOWN: - if (isPowerOnOrTransient()) { + if (isPowerOnOrTransient() && !isReboot) { onStandby(STANDBY_SHUTDOWN); } break; @@ -345,6 +348,10 @@ public class HdmiControlService extends SystemService { @Nullable private Looper mIoLooper; + // Thread safe physical address + @GuardedBy("mLock") + private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS; + // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. @@ -564,7 +571,8 @@ public class HdmiControlService extends SystemService { Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Global.MHL_INPUT_SWITCHING_ENABLED, - Global.MHL_POWER_CHARGE_ENABLED + Global.MHL_POWER_CHARGE_ENABLED, + Global.HDMI_CEC_SWITCH_ENABLED }; for (String s : settings) { resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver, @@ -605,6 +613,24 @@ public class HdmiControlService extends SystemService { if (isTvDeviceEnabled()) { tv().setSystemAudioControlFeatureEnabled(enabled); } + if (isAudioSystemDevice()) { + if (audioSystem() == null) { + Slog.e(TAG, "Audio System device has not registered yet." + + " Can't turn system audio mode on."); + break; + } + audioSystem().onSystemAduioControlFeatureSupportChanged(enabled); + } + break; + case Global.HDMI_CEC_SWITCH_ENABLED: + if (isAudioSystemDevice()) { + if (audioSystem() == null) { + Slog.w(TAG, "Switch device has not registered yet." + + " Can't turn routing on."); + break; + } + audioSystem().setRoutingControlFeatureEnables(enabled); + } break; case Global.MHL_INPUT_SWITCHING_ENABLED: setMhlInputChangeEnabled(enabled); @@ -620,6 +646,7 @@ public class HdmiControlService extends SystemService { return enabled ? ENABLED : DISABLED; } + @VisibleForTesting boolean readBooleanSetting(String key, boolean defVal) { ContentResolver cr = getContext().getContentResolver(); return Global.getInt(cr, key, toInt(defVal)) == ENABLED; @@ -630,6 +657,11 @@ public class HdmiControlService extends SystemService { Global.putInt(cr, key, toInt(value)); } + void writeStringSetting(String key, String value) { + ContentResolver cr = getContext().getContentResolver(); + Global.putString(cr, key, value); + } + private void initializeCec(int initiatedBy) { mAddressAllocated = false; mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); @@ -734,6 +766,10 @@ public class HdmiControlService extends SystemService { assertRunOnServiceThread(); HdmiPortInfo[] cecPortInfo = null; + synchronized (mLock) { + mPhysicalAddress = getPhysicalAddress(); + } + // CEC HAL provides majority of the info while MHL does only MHL support flag for // each port. Return empty array if CEC HAL didn't provide the info. if (mCecController != null) { @@ -827,7 +863,10 @@ public class HdmiControlService extends SystemService { int pathToPortId(int path) { int mask = 0xF000; int finalMask = 0xF000; - int physicalAddress = getPhysicalAddress(); + int physicalAddress; + synchronized (mLock) { + physicalAddress = mPhysicalAddress; + } int maskedAddress = physicalAddress; while (maskedAddress != 0) { @@ -1135,7 +1174,7 @@ public class HdmiControlService extends SystemService { String displayName = Build.MODEL; return new HdmiDeviceInfo(logicalAddress, getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType, - getVendorId(), displayName); + getVendorId(), displayName, powerStatus); } @ServiceThreadOnly @@ -1354,6 +1393,33 @@ public class HdmiControlService extends SystemService { HdmiCecLocalDeviceTv tv = tv(); if (tv == null) { Slog.w(TAG, "Local tv device not available"); + if (isPlaybackDevice()) { + // if playback device itself is the active source, + // return its own device info. + if (playback() != null && playback().mIsActiveSource) { + return playback().getDeviceInfo(); + } + // Otherwise get the active source and look for it from the device list + ActiveSource activeSource = mActiveSource; + // If the active source is not set yet, return null + if (!activeSource.isValid()) { + return null; + } + if (audioSystem() != null) { + HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem(); + for (HdmiDeviceInfo info : audioSystem.getSafeCecDevicesLocked()) { + if (info.getLogicalAddress() == activeSource.logicalAddress) { + return info; + } + } + } + // If the device info is not in the list yet, return a device info with minimum + // information from mActiveSource. + return new HdmiDeviceInfo(activeSource.logicalAddress, + activeSource.physicalAddress, pathToPortId(activeSource.physicalAddress), + HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0, + HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress)); + } return null; } ActiveSource activeSource = tv.getActiveSource(); @@ -1532,6 +1598,14 @@ public class HdmiControlService extends SystemService { } @Override + public int getPhysicalAddress() { + enforceAccessPermission(); + synchronized (mLock) { + return mPhysicalAddress; + } + } + + @Override public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @@ -1588,14 +1662,62 @@ public class HdmiControlService extends SystemService { public List<HdmiDeviceInfo> getDeviceList() { enforceAccessPermission(); HdmiCecLocalDeviceTv tv = tv(); - synchronized (mLock) { - return (tv == null) + if (tv != null) { + synchronized (mLock) { + return tv.getSafeCecDevicesLocked(); + } + } else { + HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem(); + synchronized (mLock) { + return (audioSystem == null) ? Collections.<HdmiDeviceInfo>emptyList() - : tv.getSafeCecDevicesLocked(); + : audioSystem.getSafeCecDevicesLocked(); + } } } @Override + public void powerOffRemoteDevice(int logicalAddress, int powerStatus) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + Slog.w(TAG, "Device " + + logicalAddress + " power status is " + powerStatus + + " before standby command sent out"); + sendCecCommand(HdmiCecMessageBuilder.buildStandby( + getRemoteControlSourceAddress(), logicalAddress)); + } + }); + } + + @Override + public void powerOnRemoteDevice(int logicalAddress, int powerStatus) { + // TODO(amyjojo): implement the method + } + + @Override + // TODO(AMYJOJO): add a result callback + public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath( + getRemoteControlSourceAddress(), physicalAddress); + if (pathToPortId(physicalAddress) != Constants.INVALID_PORT_ID) { + if (getSwitchDevice() != null) { + getSwitchDevice().handleSetStreamPath(setStreamPath); + } else { + Slog.e(TAG, "Can't get the correct local device to handle routing."); + } + } + sendCecCommand(setStreamPath); + } + }); + } + + @Override public void setSystemAudioVolume(final int oldIndex, final int newIndex, final int maxIndex) { enforceAccessPermission(); @@ -1834,14 +1956,32 @@ public class HdmiControlService extends SystemService { Slog.w(TAG, "audio system is not in system audio mode"); return; } - int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); - - sendCecCommand(HdmiCecMessageBuilder - .buildReportAudioStatus( - device.getDeviceInfo().getLogicalAddress(), - Constants.ADDR_TV, - scaledVolume, - isMute)); + audioSystem().reportAudioStatus(Constants.ADDR_TV); + } + }); + } + + @Override + public void setSystemAudioModeOnForAudioOnlySource() { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + if (!isAudioSystemDevice()) { + Slog.e(TAG, "Not an audio system device. Won't set system audio mode on"); + return; + } + if (audioSystem() == null) { + Slog.e(TAG, "Audio System local device is not registered"); + return; + } + if (!audioSystem().checkSupportAndSetSystemAudioMode(true)) { + Slog.e(TAG, "System Audio Mode is not supported."); + return; + } + sendCecCommand( + HdmiCecMessageBuilder.buildSetSystemAudioMode( + audioSystem().mAddress, Constants.ADDR_BROADCAST, true)); } }); } @@ -1851,28 +1991,52 @@ public class HdmiControlService extends SystemService { if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return; final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); pw.println("mProhibitMode: " + mProhibitMode); + pw.println("mPowerStatus: " + mPowerStatus); + + // System settings + pw.println("System_settings:"); + pw.increaseIndent(); + pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); + pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled); + pw.decreaseIndent(); + + pw.println("mMhlController: "); + pw.increaseIndent(); + mMhlController.dump(pw); + pw.decreaseIndent(); + + HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo); if (mCecController != null) { pw.println("mCecController: "); pw.increaseIndent(); mCecController.dump(pw); pw.decreaseIndent(); } + } + } - pw.println("mMhlController: "); - pw.increaseIndent(); - mMhlController.dump(pw); - pw.decreaseIndent(); + // Get the source address to send out commands to devices connected to the current device + // when other services interact with HdmiControlService. + private int getRemoteControlSourceAddress() { + if (isAudioSystemDevice()) { + return audioSystem().getDeviceInfo().getLogicalAddress(); + } else if (isPlaybackDevice()) { + return playback().getDeviceInfo().getLogicalAddress(); + } + return ADDR_UNREGISTERED; + } - pw.println("mPortInfo: "); - pw.increaseIndent(); - for (HdmiPortInfo hdmiPortInfo : mPortInfo) { - pw.println("- " + hdmiPortInfo); - } - pw.decreaseIndent(); - pw.println("mPowerStatus: " + mPowerStatus); + // Get the switch device to do CEC routing control + @Nullable + private HdmiCecLocalDeviceSource getSwitchDevice() { + if (isAudioSystemDevice()) { + return audioSystem(); + } + if (isPlaybackDevice()) { + return playback(); } + return null; } @ServiceThreadOnly @@ -2549,14 +2713,14 @@ public class HdmiControlService extends SystemService { // For example, when receiving broadcast messages, all the device types will call this // method but only one of them will be the Active Source. protected void setAndBroadcastActiveSource( - HdmiCecMessage message, int physicalAddress, int deviceType) { + int physicalAddress, int deviceType, int source) { // If the device has both playback and audio system logical addresses, // playback will claim active source. Otherwise audio system will. if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) { HdmiCecLocalDevicePlayback playback = playback(); playback.setIsActiveSource(true); playback.wakeUpIfActiveSource(); - playback.maySendActiveSource(message.getSource()); + playback.maySendActiveSource(source); setActiveSource(playback.mAddress, physicalAddress); } @@ -2567,7 +2731,7 @@ public class HdmiControlService extends SystemService { } else { audioSystem.setIsActiveSource(true); audioSystem.wakeUpIfActiveSource(); - audioSystem.maySendActiveSource(message.getSource()); + audioSystem.maySendActiveSource(source); setActiveSource(audioSystem.mAddress, physicalAddress); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index 2a8117fac68d..e44f1d1522ec 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -16,19 +16,35 @@ package com.android.server.hdmi; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import android.util.SparseArray; +import android.util.Xml; +import com.android.internal.util.HexDump; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.hdmi.Constants.AudioCodec; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Objects; /** * Various utilities to handle HDMI CEC messages. */ final class HdmiUtils { + private static final String TAG = "HdmiUtils"; + private static final int[] ADDRESS_TO_TYPE = { HdmiDeviceInfo.DEVICE_TV, // ADDR_TV HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_1 @@ -65,6 +81,12 @@ final class HdmiUtils { "Secondary_TV", }; + /** + * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)} + */ + static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1; + static final int TARGET_SAME_PHYSICAL_ADDRESS = 0; + private HdmiUtils() { /* cannot be instantiated */ } /** @@ -317,4 +339,339 @@ final class HdmiUtils { info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(), info.getVendorId(), info.getDisplayName(), newPowerStatus); } + + /** + * Dump a {@link SparseArray} to the print writer. + * + * <p>The dump is formatted: + * <pre> + * name: + * key = value + * key = value + * ... + * </pre> + */ + static <T> void dumpSparseArray(IndentingPrintWriter pw, String name, + SparseArray<T> sparseArray) { + printWithTrailingColon(pw, name); + pw.increaseIndent(); + int size = sparseArray.size(); + for (int i = 0; i < size; i++) { + int key = sparseArray.keyAt(i); + T value = sparseArray.get(key); + pw.printPair(Integer.toString(key), value); + pw.println(); + } + pw.decreaseIndent(); + } + + private static void printWithTrailingColon(IndentingPrintWriter pw, String name) { + pw.println(name.endsWith(":") ? name : name.concat(":")); + } + + /** + * Dump a {@link Map} to the print writer. + * + * <p>The dump is formatted: + * <pre> + * name: + * key = value + * key = value + * ... + * </pre> + */ + static <K, V> void dumpMap(IndentingPrintWriter pw, String name, Map<K, V> map) { + printWithTrailingColon(pw, name); + pw.increaseIndent(); + for (Map.Entry<K, V> entry: map.entrySet()) { + pw.printPair(entry.getKey().toString(), entry.getValue()); + pw.println(); + } + pw.decreaseIndent(); + } + + /** + * Dump a {@link Map} to the print writer. + * + * <p>The dump is formatted: + * <pre> + * name: + * value + * value + * ... + * </pre> + */ + static <T> void dumpIterable(IndentingPrintWriter pw, String name, Iterable<T> values) { + printWithTrailingColon(pw, name); + pw.increaseIndent(); + for (T value : values) { + pw.println(value); + } + pw.decreaseIndent(); + } + + /** + * Method to parse target physical address to the port number on the current device. + * + * <p>This check assumes target address is valid. + * + * @param targetPhysicalAddress is the physical address of the target device + * @param myPhysicalAddress is the physical address of the current device + * @return + * If the target device is under the current device, return the port number of current device + * that the target device is connected to. This also applies to the devices that are indirectly + * connected to the current device. + * + * <p>If the target device has the same physical address as the current device, return + * {@link #TARGET_SAME_PHYSICAL_ADDRESS}. + * + * <p>If the target device is not under the current device, return + * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}. + */ + public static int getLocalPortFromPhysicalAddress( + int targetPhysicalAddress, int myPhysicalAddress) { + if (myPhysicalAddress == targetPhysicalAddress) { + return TARGET_SAME_PHYSICAL_ADDRESS; + } + + int mask = 0xF000; + int finalMask = 0xF000; + int maskedAddress = myPhysicalAddress; + + while (maskedAddress != 0) { + maskedAddress = myPhysicalAddress & mask; + finalMask |= mask; + mask >>= 4; + } + + int portAddress = targetPhysicalAddress & finalMask; + if ((portAddress & (finalMask << 4)) != myPhysicalAddress) { + return TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + mask <<= 4; + int port = portAddress & mask; + while ((port >> 4) != 0) { + port >>= 4; + } + return port; + } + + public static class ShortAudioDescriptorXmlParser { + // We don't use namespaces + private static final String NS = null; + + // return a list of devices config + public static List<DeviceConfig> parse(InputStream in) + throws XmlPullParserException, IOException { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + return readDevices(parser); + } + + private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + private static List<DeviceConfig> readDevices(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<DeviceConfig> devices = new ArrayList<>(); + + parser.require(XmlPullParser.START_TAG, NS, "config"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + // Starts by looking for the device tag + if (name.equals("device")) { + String deviceType = parser.getAttributeValue(null, "type"); + DeviceConfig config = null; + if (deviceType != null) { + config = readDeviceConfig(parser, deviceType); + } + if (config != null) { + devices.add(config); + } + } else { + skip(parser); + } + } + return devices; + } + + // Processes device tags in the config. + @Nullable + private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType) + throws XmlPullParserException, IOException { + List<CodecSad> codecSads = new ArrayList<>(); + int format; + byte[] descriptor; + + parser.require(XmlPullParser.START_TAG, NS, "device"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String tagName = parser.getName(); + + // Starts by looking for the supportedFormat tag + if (tagName.equals("supportedFormat")) { + String codecAttriValue = parser.getAttributeValue(null, "format"); + String sadAttriValue = parser.getAttributeValue(null, "descriptor"); + format = (codecAttriValue) == null + ? Constants.AUDIO_CODEC_NONE : formatNameToNum(codecAttriValue); + descriptor = readSad(sadAttriValue); + if (format != Constants.AUDIO_CODEC_NONE && descriptor != null) { + codecSads.add(new CodecSad(format, descriptor)); + } + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, "supportedFormat"); + } else { + skip(parser); + } + } + if (codecSads.size() == 0) { + return null; + } + return new DeviceConfig(deviceType, codecSads); + } + + // Processes sad attribute in the supportedFormat. + @Nullable + private static byte[] readSad(String sad) { + if (sad == null || sad.length() == 0) { + return null; + } + byte[] sadBytes = HexDump.hexStringToByteArray(sad); + if (sadBytes.length != 3) { + Slog.w(TAG, "SAD byte array length is not 3. Length = " + sadBytes.length); + return null; + } + return sadBytes; + } + + @AudioCodec + private static int formatNameToNum(String codecAttriValue) { + switch (codecAttriValue) { + case "AUDIO_FORMAT_NONE": + return Constants.AUDIO_CODEC_NONE; + case "AUDIO_FORMAT_LPCM": + return Constants.AUDIO_CODEC_LPCM; + case "AUDIO_FORMAT_DD": + return Constants.AUDIO_CODEC_DD; + case "AUDIO_FORMAT_MPEG1": + return Constants.AUDIO_CODEC_MPEG1; + case "AUDIO_FORMAT_MP3": + return Constants.AUDIO_CODEC_MP3; + case "AUDIO_FORMAT_MPEG2": + return Constants.AUDIO_CODEC_MPEG2; + case "AUDIO_FORMAT_AAC": + return Constants.AUDIO_CODEC_AAC; + case "AUDIO_FORMAT_DTS": + return Constants.AUDIO_CODEC_DTS; + case "AUDIO_FORMAT_ATRAC": + return Constants.AUDIO_CODEC_ATRAC; + case "AUDIO_FORMAT_ONEBITAUDIO": + return Constants.AUDIO_CODEC_ONEBITAUDIO; + case "AUDIO_FORMAT_DDP": + return Constants.AUDIO_CODEC_DDP; + case "AUDIO_FORMAT_DTSHD": + return Constants.AUDIO_CODEC_DTSHD; + case "AUDIO_FORMAT_TRUEHD": + return Constants.AUDIO_CODEC_TRUEHD; + case "AUDIO_FORMAT_DST": + return Constants.AUDIO_CODEC_DST; + case "AUDIO_FORMAT_WMAPRO": + return Constants.AUDIO_CODEC_WMAPRO; + case "AUDIO_FORMAT_MAX": + return Constants.AUDIO_CODEC_MAX; + default: + return Constants.AUDIO_CODEC_NONE; + } + } + } + + // Device configuration of its supported Codecs and their Short Audio Descriptors. + public static class DeviceConfig { + /** Name of the device. Should be {@link Constants.AudioDevice}. **/ + public final String name; + /** List of a {@link CodecSad}. **/ + public final List<CodecSad> supportedCodecs; + + public DeviceConfig(String name, List<CodecSad> supportedCodecs) { + this.name = name; + this.supportedCodecs = supportedCodecs; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DeviceConfig) { + DeviceConfig that = (DeviceConfig) obj; + return that.name.equals(this.name) + && that.supportedCodecs.equals(this.supportedCodecs); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + name, + supportedCodecs.hashCode()); + } + } + + // Short Audio Descriptor of a specific Codec + public static class CodecSad { + /** Audio Codec. Should be {@link Constants.AudioCodec}. **/ + public final int audioCodec; + /** + * Three-byte Short Audio Descriptor. See HDMI Specification 1.4b CEC 13.15.3 and + * ANSI-CTA-861-F-FINAL 7.5.2 Audio Data Block for more details. + */ + public final byte[] sad; + + public CodecSad(int audioCodec, byte[] sad) { + this.audioCodec = audioCodec; + this.sad = sad; + } + + public CodecSad(int audioCodec, String sad) { + this.audioCodec = audioCodec; + this.sad = HexDump.hexStringToByteArray(sad); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CodecSad) { + CodecSad that = (CodecSad) obj; + return that.audioCodec == this.audioCodec + && Arrays.equals(that.sad, this.sad); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + audioCodec, + Arrays.hashCode(sad)); + } + } } diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index 41bf01f842cd..354d8d1e43e0 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -92,6 +92,9 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { if (source.mService.audioSystem() != null) { source = source.mService.audioSystem(); } + if (source.getLocalActivePort() != Constants.CEC_SWITCH_HOME) { + source.switchInputOnReceivingNewActivePath(getSourcePath()); + } source.setRoutingPort(Constants.CEC_SWITCH_HOME); source.setLocalActivePort(Constants.CEC_SWITCH_HOME); } diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java index ac2dbdf9450e..c16d1b4ecec5 100644 --- a/services/core/java/com/android/server/job/controllers/QuotaController.java +++ b/services/core/java/com/android/server/job/controllers/QuotaController.java @@ -26,7 +26,10 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AlarmManager; +import android.app.IUidObserver; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; @@ -38,12 +41,14 @@ import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -69,6 +74,11 @@ import java.util.function.Predicate; * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately * applied to it. * + * Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run + * freely when an app enters the foreground state and are restricted when the app leaves the + * foreground state. However, jobs that are started while the app is in the TOP state are not + * restricted regardless of the app's state change. + * * Test: atest com.android.server.job.controllers.QuotaControllerTest */ public final class QuotaController extends StateController { @@ -97,6 +107,12 @@ public final class QuotaController extends StateController { data.put(packageName, obj); } + public void clear() { + for (int i = 0; i < mData.size(); ++i) { + mData.valueAt(i).clear(); + } + } + /** Removes all the data for the user, if there was any. */ public void delete(int userId) { mData.delete(userId); @@ -119,6 +135,11 @@ public final class QuotaController extends StateController { return null; } + /** @see SparseArray#indexOfKey */ + public int indexOfKey(int userId) { + return mData.indexOfKey(userId); + } + /** Returns the userId at the given index. */ public int keyAt(int index) { return mData.keyAt(index); @@ -294,6 +315,17 @@ public final class QuotaController extends StateController { /** Cached calculation results for each app, with the standby buckets as the array indices. */ private final UserPackageMap<ExecutionStats[]> mExecutionStatsCache = new UserPackageMap<>(); + /** List of UIDs currently in the foreground. */ + private final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); + + /** + * List of jobs that started while the UID was in the TOP state. There will be no more than + * 16 ({@link JobSchedulerService.MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is + * fine. + */ + private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); + + private final ActivityManagerInternal mActivityManagerInternal; private final AlarmManager mAlarmManager; private final ChargingTracker mChargeTracker; private final Handler mHandler; @@ -343,6 +375,29 @@ public final class QuotaController extends StateController { } }; + private final IUidObserver mUidObserver = new IUidObserver.Stub() { + @Override + public void onUidStateChanged(int uid, int procState, long procStateSeq) { + mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget(); + } + + @Override + public void onUidGone(int uid, boolean disabled) { + } + + @Override + public void onUidActive(int uid) { + } + + @Override + public void onUidIdle(int uid, boolean disabled) { + } + + @Override + public void onUidCachedChanged(int uid, boolean cached) { + } + }; + /** * The rolling window size for each standby bucket. Within each window, an app will have 10 * minutes to run its jobs. @@ -363,12 +418,15 @@ public final class QuotaController extends StateController { private static final int MSG_CLEAN_UP_SESSIONS = 1; /** Check if a package is now within its quota. */ private static final int MSG_CHECK_PACKAGE = 2; + /** Process state for a UID has changed. */ + private static final int MSG_UID_PROCESS_STATE_CHANGED = 3; public QuotaController(JobSchedulerService service) { super(service); mHandler = new QcHandler(mContext.getMainLooper()); mChargeTracker = new ChargingTracker(); mChargeTracker.startTracking(); + mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); // Set up the app standby bucketing tracker @@ -376,6 +434,14 @@ public final class QuotaController extends StateController { UsageStatsManagerInternal.class); usageStats.addAppIdleStateChangeListener(new StandbyTracker()); + try { + ActivityManager.getService().registerUidObserver(mUidObserver, + ActivityManager.UID_OBSERVER_PROCSTATE, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null); + } catch (RemoteException e) { + // ignored; both services live in system_server + } + onConstantsUpdatedLocked(); } @@ -399,11 +465,15 @@ public final class QuotaController extends StateController { if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString()); final int userId = jobStatus.getSourceUserId(); final String packageName = jobStatus.getSourcePackageName(); + final int uid = jobStatus.getSourceUid(); Timer timer = mPkgTimers.get(userId, packageName); if (timer == null) { - timer = new Timer(userId, packageName); + timer = new Timer(uid, userId, packageName); mPkgTimers.add(userId, packageName, timer); } + if (mActivityManagerInternal.getUidProcessState(uid) == ActivityManager.PROCESS_STATE_TOP) { + mTopStartedJobs.add(jobStatus); + } timer.startTrackingJob(jobStatus); } @@ -421,6 +491,7 @@ public final class QuotaController extends StateController { if (jobs != null) { jobs.remove(jobStatus); } + mTopStartedJobs.remove(jobStatus); } } @@ -511,6 +582,7 @@ public final class QuotaController extends StateController { mInQuotaAlarmListeners.delete(userId, packageName); } mExecutionStatsCache.delete(userId, packageName); + mForegroundUids.delete(uid); } @Override @@ -522,6 +594,20 @@ public final class QuotaController extends StateController { mExecutionStatsCache.delete(userId); } + private boolean isUidInForeground(int uid) { + if (UserHandle.isCore(uid)) { + return true; + } + synchronized (mLock) { + return mForegroundUids.get(uid); + } + } + + /** @return true if the job was started while the app was in the TOP state. */ + private boolean isTopStartedJob(@NonNull final JobStatus jobStatus) { + return mTopStartedJobs.contains(jobStatus); + } + /** * Returns an appropriate standby bucket for the job, taking into account any standby * exemptions. @@ -537,9 +623,15 @@ public final class QuotaController extends StateController { private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { final int standbyBucket = getEffectiveStandbyBucket(jobStatus); - // Jobs for the active app should always be able to run. - return jobStatus.uidActive || isWithinQuotaLocked( - jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); + Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); + // A job is within quota if one of the following is true: + // 1. it was started while the app was in the TOP state + // 2. the app is currently in the foreground + // 3. the app overall is within its quota + return isTopStartedJob(jobStatus) + || isUidInForeground(jobStatus.getSourceUid()) + || isWithinQuotaLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); } private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, @@ -800,7 +892,7 @@ public final class QuotaController extends StateController { final boolean isCharging = mChargeTracker.isCharging(); if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging); // Deal with Timers first. - mPkgTimers.forEach((t) -> t.onChargingChanged(nowElapsed, isCharging)); + mPkgTimers.forEach((t) -> t.onStateChanged(nowElapsed, isCharging)); // Now update jobs. maybeUpdateAllConstraintsLocked(); } @@ -837,10 +929,15 @@ public final class QuotaController extends StateController { boolean changed = false; for (int i = jobs.size() - 1; i >= 0; --i) { final JobStatus js = jobs.valueAt(i); - if (js.uidActive) { - // Jobs for the active app should always be able to run. + if (isTopStartedJob(js)) { + // Job was started while the app was in the TOP state so we should allow it to + // finish. changed |= js.setQuotaConstraintSatisfied(true); - } else if (realStandbyBucket == getEffectiveStandbyBucket(js)) { + } else if (realStandbyBucket != ACTIVE_INDEX + && realStandbyBucket == getEffectiveStandbyBucket(js)) { + // An app in the ACTIVE bucket may be out of quota while the job could be in quota + // for some reason. Therefore, avoid setting the real value here and check each job + // individually. changed |= js.setQuotaConstraintSatisfied(realInQuota); } else { // This job is somehow exempted. Need to determine its own quota status. @@ -854,7 +951,7 @@ public final class QuotaController extends StateController { maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket); } else { QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName); - if (alarmListener != null) { + if (alarmListener != null && alarmListener.isWaiting()) { mAlarmManager.cancel(alarmListener); // Set the trigger time to 0 so that the alarm doesn't think it's still waiting. alarmListener.setTriggerTime(0); @@ -863,6 +960,56 @@ public final class QuotaController extends StateController { return changed; } + private class UidConstraintUpdater implements Consumer<JobStatus> { + private final UserPackageMap<Integer> mToScheduleStartAlarms = new UserPackageMap<>(); + public boolean wasJobChanged; + + @Override + public void accept(JobStatus jobStatus) { + wasJobChanged |= jobStatus.setQuotaConstraintSatisfied(isWithinQuotaLocked(jobStatus)); + final int userId = jobStatus.getSourceUserId(); + final String packageName = jobStatus.getSourcePackageName(); + final int realStandbyBucket = jobStatus.getStandbyBucket(); + if (isWithinQuotaLocked(userId, packageName, realStandbyBucket)) { + QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName); + if (alarmListener != null && alarmListener.isWaiting()) { + mAlarmManager.cancel(alarmListener); + // Set the trigger time to 0 so that the alarm doesn't think it's still waiting. + alarmListener.setTriggerTime(0); + } + } else { + mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket); + } + } + + void postProcess() { + for (int u = 0; u < mToScheduleStartAlarms.numUsers(); ++u) { + final int userId = mToScheduleStartAlarms.keyAt(u); + for (int p = 0; p < mToScheduleStartAlarms.numPackagesForUser(userId); ++p) { + final String packageName = mToScheduleStartAlarms.keyAt(u, p); + final int standbyBucket = mToScheduleStartAlarms.get(userId, packageName); + maybeScheduleStartAlarmLocked(userId, packageName, standbyBucket); + } + } + } + + void reset() { + wasJobChanged = false; + mToScheduleStartAlarms.clear(); + } + } + + private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater(); + + private boolean maybeUpdateConstraintForUidLocked(final int uid) { + mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints); + + mUpdateUidConstraints.postProcess(); + boolean changed = mUpdateUidConstraints.wasJobChanged; + mUpdateUidConstraints.reset(); + return changed; + } + /** * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run * again. This should only be called if the package is already out of quota. @@ -1052,6 +1199,7 @@ public final class QuotaController extends StateController { private final class Timer { private final Package mPkg; + private final int mUid; // List of jobs currently running for this app that started when the app wasn't in the // foreground. @@ -1059,16 +1207,18 @@ public final class QuotaController extends StateController { private long mStartTimeElapsed; private int mBgJobCount; - Timer(int userId, String packageName) { + Timer(int uid, int userId, String packageName) { mPkg = new Package(userId, packageName); + mUid = uid; } void startTrackingJob(@NonNull JobStatus jobStatus) { - if (jobStatus.uidActive) { - // We intentionally don't pay attention to fg state changes after a job has started. + if (isTopStartedJob(jobStatus)) { + // We intentionally don't pay attention to fg state changes after a TOP job has + // started. if (DEBUG) { Slog.v(TAG, - "Timer ignoring " + jobStatus.toShortString() + " because uidActive"); + "Timer ignoring " + jobStatus.toShortString() + " because isTop"); } return; } @@ -1076,7 +1226,7 @@ public final class QuotaController extends StateController { synchronized (mLock) { // Always track jobs, even when charging. mRunningBgJobs.add(jobStatus); - if (!mChargeTracker.isCharging()) { + if (shouldTrackLocked()) { mBgJobCount++; if (mRunningBgJobs.size() == 1) { // Started tracking the first job. @@ -1142,6 +1292,10 @@ public final class QuotaController extends StateController { } } + boolean isRunning(JobStatus jobStatus) { + return mRunningBgJobs.contains(jobStatus); + } + long getCurrentDuration(long nowElapsed) { synchronized (mLock) { return !isActive() ? 0 : nowElapsed - mStartTimeElapsed; @@ -1154,17 +1308,21 @@ public final class QuotaController extends StateController { } } - void onChargingChanged(long nowElapsed, boolean isCharging) { + private boolean shouldTrackLocked() { + return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid); + } + + void onStateChanged(long nowElapsed, boolean isQuotaFree) { synchronized (mLock) { - if (isCharging) { + if (isQuotaFree) { emitSessionLocked(nowElapsed); - } else { + } else if (shouldTrackLocked()) { // Start timing from unplug. if (mRunningBgJobs.size() > 0) { mStartTimeElapsed = nowElapsed; // NOTE: this does have the unfortunate consequence that if the device is - // repeatedly plugged in and unplugged, the job count for a package may be - // artificially high. + // repeatedly plugged in and unplugged, or an app changes foreground state + // very frequently, the job count for a package may be artificially high. mBgJobCount = mRunningBgJobs.size(); // Starting the timer means that all cached execution stats are now // incorrect. @@ -1371,6 +1529,38 @@ public final class QuotaController extends StateController { } break; } + case MSG_UID_PROCESS_STATE_CHANGED: { + final int uid = msg.arg1; + final int procState = msg.arg2; + final int userId = UserHandle.getUserId(uid); + final long nowElapsed = sElapsedRealtimeClock.millis(); + + synchronized (mLock) { + boolean isQuotaFree; + if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + mForegroundUids.put(uid, true); + isQuotaFree = true; + } else { + mForegroundUids.delete(uid); + isQuotaFree = false; + } + // Update Timers first. + final int userIndex = mPkgTimers.indexOfKey(userId); + if (userIndex != -1) { + final int numPkgs = mPkgTimers.numPackagesForUser(userId); + for (int p = 0; p < numPkgs; ++p) { + Timer t = mPkgTimers.valueAt(userIndex, p); + if (t != null) { + t.onStateChanged(nowElapsed, isQuotaFree); + } + } + } + if (maybeUpdateConstraintForUidLocked(uid)) { + mStateChangedListener.onControllerStateChanged(); + } + } + break; + } } } } @@ -1420,6 +1610,12 @@ public final class QuotaController extends StateController { @VisibleForTesting @NonNull + SparseBooleanArray getForegroundUids() { + return mForegroundUids; + } + + @VisibleForTesting + @NonNull Handler getHandler() { return mHandler; } @@ -1450,6 +1646,10 @@ public final class QuotaController extends StateController { pw.println("In parole: " + mInParole); pw.println(); + pw.print("Foreground UIDs: "); + pw.println(mForegroundUids.toString()); + pw.println(); + mTrackedJobs.forEach((jobs) -> { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); @@ -1460,6 +1660,9 @@ public final class QuotaController extends StateController { js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); + if (mTopStartedJobs.contains(js)) { + pw.print(" (TOP)"); + } pw.println(); pw.increaseIndent(); @@ -1511,6 +1714,11 @@ public final class QuotaController extends StateController { proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging()); proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole); + for (int i = 0; i < mForegroundUids.size(); ++i) { + proto.write(StateControllerProto.QuotaController.FOREGROUND_UIDS, + mForegroundUids.keyAt(i)); + } + mTrackedJobs.forEach((jobs) -> { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); @@ -1526,6 +1734,8 @@ public final class QuotaController extends StateController { proto.write( StateControllerProto.QuotaController.TrackedJob.EFFECTIVE_STANDBY_BUCKET, getEffectiveStandbyBucket(js)); + proto.write(StateControllerProto.QuotaController.TrackedJob.IS_TOP_STARTED_JOB, + mTopStartedJobs.contains(js)); proto.write(StateControllerProto.QuotaController.TrackedJob.HAS_QUOTA, js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS, diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java index 29465ad366f4..9e339430da12 100644 --- a/services/core/java/com/android/server/location/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/GnssConfiguration.java @@ -29,7 +29,10 @@ import libcore.io.IoUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -66,6 +69,7 @@ class GnssConfiguration { "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL"; private static final String CONFIG_GPS_LOCK = "GPS_LOCK"; private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC"; + public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS"; // Limit on NI emergency mode time extension after emergency sessions ends private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum @@ -171,6 +175,32 @@ class GnssConfiguration { } /** + * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS or + * {@Collections.EMPTY_LIST} if no value is provided. + */ + List<String> getProxyApps() { + // Space separated list of Android proxy app package names. + String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS); + if (TextUtils.isEmpty(proxyAppsStr)) { + return Collections.EMPTY_LIST; + } + + String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+"); + if (proxyAppsArray.length == 0) { + return Collections.EMPTY_LIST; + } + + // TODO(b/122856486): Validate proxy app names so that a system app or some popular app + // with location permission is not specified as a proxy app. + ArrayList proxyApps = new ArrayList(proxyAppsArray.length); + for (String proxyApp : proxyAppsArray) { + proxyApps.add(proxyApp); + } + + return proxyApps; + } + + /** * Updates the GNSS HAL satellite blacklist. */ void setSatelliteBlacklist(int[] constellations, int[] svids) { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 269767ac3be2..d346ddcc5264 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -64,6 +64,7 @@ import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.util.Log; +import android.util.StatsLog; import com.android.internal.app.IBatteryStats; import com.android.internal.location.GpsNetInitiatedHandler; @@ -371,6 +372,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private boolean mSuplEsEnabled = false; private final Context mContext; + private final Looper mLooper; private final LocationExtras mLocationExtras = new LocationExtras(); private final GnssStatusListenerHelper mGnssStatusListenerHelper; private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper; @@ -381,6 +383,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final NtpTimeHelper mNtpTimeHelper; private final GnssBatchingProvider mGnssBatchingProvider; private final GnssGeofenceProvider mGnssGeofenceProvider; + private GnssVisibilityControl mGnssVisibilityControl; // Handler for processing events private Handler mHandler; @@ -555,6 +558,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT); mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec()); mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1; + if (mGnssVisibilityControl != null) { + mGnssVisibilityControl.updateProxyApps(mGnssConfiguration.getProxyApps()); + } } public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager, @@ -562,6 +568,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements super(locationProviderManager); mContext = context; + mLooper = looper; // Create a wake lock mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -1704,6 +1711,24 @@ public class GnssLocationProvider extends AbstractLocationProvider implements ", response: " + userResponse); } native_send_ni_response(notificationId, userResponse); + + StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED, + StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE, + notificationId, + /* niType= */ 0, + /* needNotify= */ false, + /* needVerify= */ false, + /* privacyOverride= */ false, + /* timeout= */ 0, + /* defaultResponse= */ 0, + /* requestorId= */ null, + /* text= */ null, + /* requestorIdEncoding= */ 0, + /* textEncoding= */ 0, + mSuplEsEnabled, + mEnabled, + userResponse); + return true; } }; @@ -1753,6 +1778,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements notification.textEncoding = textEncoding; mNIHandler.handleNiNotification(notification); + StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED, + StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST, + notification.notificationId, + notification.niType, + notification.needNotify, + notification.needVerify, + notification.privacyOverride, + notification.timeout, + notification.defaultResponse, + notification.requestorId, + notification.text, + notification.requestorIdEncoding, + notification.textEncoding, + mSuplEsEnabled, + mEnabled, + /* userResponse= */ 0); } /** @@ -1834,6 +1875,27 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } + // Implements method nfwNotifyCb() in IGnssVisibilityControlCallback.hal. + @NativeEntryPoint + private void reportNfwNotification(String proxyAppPackageName, byte protocolStack, + String otherProtocolStackName, byte requestor, String requestorId, byte responseType, + boolean inEmergencyMode, boolean isCachedLocation) { + if (mGnssVisibilityControl == null) { + Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl is not initialized."); + return; + } + + mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack, + otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode, + isCachedLocation); + } + + // Implements method isInEmergencySession() in IGnssVisibilityControlCallback.hal. + @NativeEntryPoint + boolean isInEmergencySession() { + return mNIHandler.getInEmergency(); + } + private void sendMessage(int message, int arg, Object obj) { // hold a wake lock until this message is delivered // note that this assumes the message will not be removed from the queue before @@ -1922,6 +1984,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements native_cleanup(); } + if (native_is_gnss_visibility_control_supported()) { + mGnssVisibilityControl = new GnssVisibilityControl(mContext, mLooper); + } + // load default GPS configuration // (this configuration might change in the future based on SIM changes) reloadGpsProperties(); @@ -2079,6 +2145,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static native boolean native_is_supported(); + private static native boolean native_is_gnss_visibility_control_supported(); + private static native void native_init_once(); private native boolean native_init(); diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java new file mode 100644 index 000000000000..845aa9dfacd6 --- /dev/null +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -0,0 +1,362 @@ +/* + * 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.location; + +import android.annotation.SuppressLint; +import android.app.AppOpsManager; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Handles GNSS non-framework location access user visibility and control. + * + * The state of the GnssVisibilityControl object must be accessed/modified through the Handler + * thread only. + */ +class GnssVisibilityControl { + private static final String TAG = "GnssVisibilityControl"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + // Constants related to non-framework (NFW) location access permission proxy apps. + private static final String NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX = + ".NonFrameworkLocationAccessActivity"; + private static final String NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX = + ".intent.action.NON_FRAMEWORK_LOCATION_ACCESS"; + private static final String NFW_INTENT_TYPE = "text/plain"; + + private static final String LOCATION_PERMISSION_NAME = + "android.permission.ACCESS_FINE_LOCATION"; + + // Wakelocks + private static final String WAKELOCK_KEY = TAG; + private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; + private final PowerManager.WakeLock mWakeLock; + + private final AppOpsManager mAppOps; + private final PackageManager mPackageManager; + + private final Handler mHandler; + private final Context mContext; + + // Number of non-framework location access proxy apps is expected to be small (< 5). + private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7; + private HashMap<String, Boolean> mProxyAppToLocationPermissions = new HashMap<>( + HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS); + + private PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener = + uid -> postEvent(() -> handlePermissionsChanged(uid)); + + GnssVisibilityControl(Context context, Looper looper) { + mContext = context; + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); + mHandler = new Handler(looper); + mAppOps = mContext.getSystemService(AppOpsManager.class); + mPackageManager = mContext.getPackageManager(); + // TODO(b/122855984): Handle global location settings on/off. + // TODO(b/122856189): Handle roaming case. + } + + void updateProxyApps(List<String> nfwLocationAccessProxyApps) { + // NOTE: This class doesn't explicitly register and listen for SIM_STATE_CHANGED event + // but rather piggy backs on the GnssLocationProvider SIM_STATE_CHANGED handling + // so that the order of processing is preserved. GnssLocationProvider should + // first load the new config parameters for the new SIM and then call this method. + postEvent(() -> handleSubscriptionOrSimChanged(nfwLocationAccessProxyApps)); + } + + void reportNfwNotification(String proxyAppPackageName, byte protocolStack, + String otherProtocolStackName, byte requestor, String requestorId, byte responseType, + boolean inEmergencyMode, boolean isCachedLocation) { + postEvent(() -> handleNfwNotification( + new NfwNotification(proxyAppPackageName, protocolStack, otherProtocolStackName, + requestor, requestorId, responseType, inEmergencyMode, isCachedLocation))); + } + + private void handleSubscriptionOrSimChanged(List<String> nfwLocationAccessProxyApps) { + if (nfwLocationAccessProxyApps.isEmpty()) { + // Stop listening for app permission changes. Clear the app list in GNSS HAL. + if (!mProxyAppToLocationPermissions.isEmpty()) { + mPackageManager.removeOnPermissionsChangeListener(mOnPermissionsChangedListener); + mProxyAppToLocationPermissions.clear(); + updateNfwLocationAccessProxyAppsInGnssHal(); + } + return; + } + + if (mProxyAppToLocationPermissions.isEmpty()) { + mPackageManager.addOnPermissionsChangeListener(mOnPermissionsChangedListener); + } else { + mProxyAppToLocationPermissions.clear(); + } + + for (String proxApp : nfwLocationAccessProxyApps) { + mProxyAppToLocationPermissions.put(proxApp, hasLocationPermission(proxApp)); + } + + updateNfwLocationAccessProxyAppsInGnssHal(); + } + + // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal + private static class NfwNotification { + private static final String KEY_PROTOCOL_STACK = "ProtocolStack"; + private static final String KEY_OTHER_PROTOCOL_STACK_NAME = "OtherProtocolStackName"; + private static final String KEY_REQUESTOR = "Requestor"; + private static final String KEY_REQUESTOR_ID = "RequestorId"; + private static final String KEY_RESPONSE_TYPE = "ResponseType"; + private static final String KEY_IN_EMERGENCY_MODE = "InEmergencyMode"; + private static final String KEY_IS_CACHED_LOCATION = "IsCachedLocation"; + + // This must match with NfwResponseType enum in IGnssVisibilityControlCallback.hal. + private static final byte NFW_RESPONSE_TYPE_REJECTED = 0; + private static final byte NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED = 1; + private static final byte NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED = 2; + + private final String mProxyAppPackageName; + private final byte mProtocolStack; + private final String mOtherProtocolStackName; + private final byte mRequestor; + private final String mRequestorId; + private final byte mResponseType; + private final boolean mInEmergencyMode; + private final boolean mIsCachedLocation; + + NfwNotification(String proxyAppPackageName, byte protocolStack, + String otherProtocolStackName, byte requestor, String requestorId, + byte responseType, boolean inEmergencyMode, boolean isCachedLocation) { + mProxyAppPackageName = proxyAppPackageName; + mProtocolStack = protocolStack; + mOtherProtocolStackName = otherProtocolStackName; + mRequestor = requestor; + mRequestorId = requestorId; + mResponseType = responseType; + mInEmergencyMode = inEmergencyMode; + mIsCachedLocation = isCachedLocation; + } + + void copyFieldsToIntent(Intent intent) { + intent.putExtra(KEY_PROTOCOL_STACK, mProtocolStack); + if (!TextUtils.isEmpty(mOtherProtocolStackName)) { + intent.putExtra(KEY_OTHER_PROTOCOL_STACK_NAME, mOtherProtocolStackName); + } + intent.putExtra(KEY_REQUESTOR, mRequestor); + if (!TextUtils.isEmpty(mRequestorId)) { + intent.putExtra(KEY_REQUESTOR_ID, mRequestorId); + } + intent.putExtra(KEY_RESPONSE_TYPE, mResponseType); + intent.putExtra(KEY_IN_EMERGENCY_MODE, mInEmergencyMode); + if (mResponseType == NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED) { + intent.putExtra(KEY_IS_CACHED_LOCATION, mIsCachedLocation); + } + } + + @SuppressLint("DefaultLocale") + public String toString() { + return String.format( + "[Notification] proxyAppPackageName: %s, protocolStack: %d" + + ", otherProtocolStackName: %s, requestor: %d, requestorId: %s" + + ", responseType: %d, inEmergencyMode: %b, isCachedLocation: %b", + mProxyAppPackageName, mProtocolStack, mOtherProtocolStackName, + mRequestor, mRequestorId, mResponseType, mInEmergencyMode, mIsCachedLocation); + } + + String getResponseTypeAsString() { + switch (mResponseType) { + case NFW_RESPONSE_TYPE_REJECTED: + return "REJECTED"; + case NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED: + return "ACCEPTED_NO_LOCATION_PROVIDED"; + case NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED: + return "ACCEPTED_LOCATION_PROVIDED"; + default: + return "<Unknown>"; + } + } + } + + private void handlePermissionsChanged(int uid) { + if (mProxyAppToLocationPermissions.isEmpty()) { + return; + } + + for (Map.Entry<String, Boolean> entry : mProxyAppToLocationPermissions.entrySet()) { + // Cannot cache uid since the application could be uninstalled and reinstalled. + final String proxyApp = entry.getKey(); + final Integer nfwProxyAppUid = getApplicationUid(proxyApp); + if (nfwProxyAppUid == null || nfwProxyAppUid != uid) { + continue; + } + + final boolean isLocationPermissionEnabled = hasLocationPermission(proxyApp); + if (isLocationPermissionEnabled != entry.getValue()) { + Log.i(TAG, "Location permission setting is changed to " + + (isLocationPermissionEnabled ? "enabled" : "disabled") + + " for non-framework location access proxy app " + + proxyApp); + entry.setValue(isLocationPermissionEnabled); + updateNfwLocationAccessProxyAppsInGnssHal(); + return; + } + } + } + + private Integer getApplicationUid(String pkgName) { + try { + return mPackageManager.getApplicationInfo(pkgName, 0).uid; + } catch (PackageManager.NameNotFoundException e) { + if (DEBUG) { + Log.d(TAG, "Non-framework location access proxy app " + + pkgName + " is not found."); + } + return null; + } + } + + private boolean hasLocationPermission(String pkgName) { + return mPackageManager.checkPermission(LOCATION_PERMISSION_NAME, pkgName) + == PackageManager.PERMISSION_GRANTED; + } + + private void updateNfwLocationAccessProxyAppsInGnssHal() { + // Get a count of proxy apps with location permission enabled to array creation size. + int countLocationPermissionEnabledProxyApps = 0; + for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) { + if (hasLocationPermissionEnabled) { + ++countLocationPermissionEnabledProxyApps; + } + } + + int i = 0; + String[] locationPermissionEnabledProxyApps = + new String[countLocationPermissionEnabledProxyApps]; + for (Map.Entry<String, Boolean> entry : mProxyAppToLocationPermissions.entrySet()) { + final String proxyApp = entry.getKey(); + final boolean hasLocationPermissionEnabled = entry.getValue(); + if (hasLocationPermissionEnabled) { + locationPermissionEnabledProxyApps[i++] = proxyApp; + } + } + + String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps); + Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: " + + proxyAppsStr); + boolean result = native_enable_nfw_location_access(locationPermissionEnabledProxyApps); + if (!result) { + Log.e(TAG, "Failed to update non-framework location access proxy apps in the" + + " GNSS HAL to: " + proxyAppsStr); + } + } + + private void handleNfwNotification(NfwNotification nfwNotification) { + if (DEBUG) Log.d(TAG, nfwNotification.toString()); + + final String proxyAppPackageName = nfwNotification.mProxyAppPackageName; + if (TextUtils.isEmpty(proxyAppPackageName)) { + Log.e(TAG, "ProxyAppPackageName field is not set. Not sending intent to proxy app for " + + nfwNotification); + return; + } + + Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get( + proxyAppPackageName); + if (isLocationPermissionEnabled == null) { + // App is not in the configured list. + Log.e(TAG, "Could not find proxy app with name: " + proxyAppPackageName + " in the " + + "value specified for config parameter: " + + GnssConfiguration.CONFIG_NFW_PROXY_APPS + ". Not sending intent to proxy app" + + " for " + nfwNotification); + return; + } + + // Send intent to non-framework location proxy app with notification information. + final Intent intent = new Intent( + proxyAppPackageName + NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX); + final String proxAppActivityName = + proxyAppPackageName + NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX; + intent.setClassName(proxyAppPackageName, proxAppActivityName); + intent.setType(NFW_INTENT_TYPE); + nfwNotification.copyFieldsToIntent(intent); + + // Check if the proxy app is still installed. + final Integer clsAppUid = getApplicationUid(proxyAppPackageName); + if (clsAppUid == null || intent.resolveActivity(mPackageManager) == null) { + Log.i(TAG, "Proxy application " + proxyAppPackageName + " and/or activity " + + proxAppActivityName + " is not found. Not sending" + + " intent to proxy app for " + nfwNotification); + return; + } + + // Display location icon attributed to this proxy app. + mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, clsAppUid, proxyAppPackageName); + + // Log proxy app permission mismatch between framework and GNSS HAL. + boolean isLocationRequestAccepted = + nfwNotification.mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED; + if (isLocationPermissionEnabled != isLocationRequestAccepted) { + Log.w(TAG, "Permission mismatch. Framework proxy app " + proxyAppPackageName + + " location permission is set to " + isLocationPermissionEnabled + + " but GNSS non-framework location access response type is " + + nfwNotification.getResponseTypeAsString() + " for " + nfwNotification); + } + + // Notify proxy app. + try { + if (DEBUG) { + Log.d(TAG, "Sending non-framework location access notification intent: " + intent); + } + mContext.startActivityAsUser(intent, UserHandle.getUserHandleForUid(clsAppUid)); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "Activity not found. Failed to send non-framework location access" + + " notification intent to proxy app activity: " + proxAppActivityName); + } + } + + private void postEvent(Runnable event) { + // Hold a wake lock until this message is delivered. + // Note that this assumes the message will not be removed from the queue before + // it is handled (otherwise the wake lock would be leaked). + mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS); + if (!mHandler.post(runEventAndReleaseWakeLock(event))) { + mWakeLock.release(); + } + } + + private Runnable runEventAndReleaseWakeLock(Runnable event) { + return () -> { + try { + event.run(); + } finally { + mWakeLock.release(); + } + }; + } + + private native boolean native_enable_nfw_location_access(String[] proxyApps); +} diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index 86bc9f313551..fe91c63e712b 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -52,7 +52,6 @@ public class MockProvider extends AbstractLocationProvider { mExtras = null; setProperties(properties); - setEnabled(true); } /** Sets the enabled state of this mock provider. */ diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index d611a172d225..7fffe8eb402a 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -80,7 +80,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final ControllerStub mController; private final SessionStub mSession; private final SessionCb mSessionCb; - private final MediaSessionService mService; + private final MediaSessionService.ServiceImpl mService; private final Context mContext; private final Object mLock = new Object(); @@ -120,7 +120,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private String mMetadataDescription; public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, - SessionCallbackLink cb, String tag, MediaSessionService service, Looper handlerLooper) { + SessionCallbackLink cb, String tag, MediaSessionService.ServiceImpl service, + Looper handlerLooper) { mOwnerPid = ownerPid; mOwnerUid = ownerUid; mUserId = userId; diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index ba7b87e6f029..d20ed8c11009 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -16,79 +16,15 @@ package com.android.server.media; -import static android.os.UserHandle.USER_ALL; - -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.INotificationManager; -import android.app.KeyguardManager; -import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ServiceInfo; -import android.content.pm.UserInfo; -import android.database.ContentObserver; -import android.media.AudioManager; -import android.media.AudioManagerInternal; -import android.media.AudioPlaybackConfiguration; -import android.media.AudioSystem; -import android.media.IAudioService; -import android.media.IRemoteVolumeController; -import android.media.MediaController2; -import android.media.Session2CommandGroup; import android.media.Session2Token; -import android.media.session.IActiveSessionsListener; -import android.media.session.ICallback; -import android.media.session.IOnMediaKeyListener; -import android.media.session.IOnVolumeKeyLongPressListener; -import android.media.session.ISession; -import android.media.session.ISession2TokensListener; -import android.media.session.ISessionManager; -import android.media.session.MediaSession; -import android.media.session.MediaSessionManager; -import android.media.session.SessionCallbackLink; -import android.net.Uri; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerExecutor; import android.os.IBinder; -import android.os.Message; -import android.os.PowerManager; -import android.os.Process; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.speech.RecognizerIntent; -import android.text.TextUtils; import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.KeyEvent; -import android.view.ViewConfiguration; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.DumpUtils; -import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.Watchdog.Monitor; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; import java.util.List; /** @@ -97,2040 +33,124 @@ import java.util.List; public class MediaSessionService extends SystemService implements Monitor { private static final String TAG = "MediaSessionService"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - // Leave log for key event always. - private static final boolean DEBUG_KEY_EVENT = true; - - private static final int WAKELOCK_TIMEOUT = 5000; - private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; - - private final SessionManagerImpl mSessionManagerImpl; - private final MessageHandler mHandler = new MessageHandler(); - private final PowerManager.WakeLock mMediaEventWakeLock; - private final int mLongPressTimeout; - private final INotificationManager mNotificationManager; - private final Object mLock = new Object(); - // Keeps the full user id for each user. - @GuardedBy("mLock") - private final SparseIntArray mFullUserIds = new SparseIntArray(); - @GuardedBy("mLock") - private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>(); - @GuardedBy("mLock") - private final ArrayList<SessionsListenerRecord> mSessionsListeners - = new ArrayList<SessionsListenerRecord>(); - // Map user id as index to list of Session2Tokens - // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in - // one place. - @GuardedBy("mLock") - private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>(); - @GuardedBy("mLock") - private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords = - new ArrayList<>(); - private KeyguardManager mKeyguardManager; - private IAudioService mAudioService; - private AudioManagerInternal mAudioManagerInternal; - private ActivityManager mActivityManager; - private ContentResolver mContentResolver; - private SettingsObserver mSettingsObserver; - private boolean mHasFeatureLeanback; - - // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) - // It's always not null after the MediaSessionService is started. - private FullUserRecord mCurrentFullUserRecord; - private MediaSessionRecord mGlobalPrioritySession; - private AudioPlayerStateMonitor mAudioPlayerStateMonitor; - - // Used to notify system UI when remote volume was changed. TODO find a - // better way to handle this. - private IRemoteVolumeController mRvc; + private final ServiceImpl mImpl; public MediaSessionService(Context context) { super(context); - mSessionManagerImpl = new SessionManagerImpl(); - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); - mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); - mNotificationManager = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mImpl = new MediaSessionServiceImpl(context); } @Override public void onStart() { - publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); + publishBinderService(Context.MEDIA_SESSION_SERVICE, mImpl.getServiceBinder()); Watchdog.getInstance().addMonitor(this); - mKeyguardManager = - (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); - mAudioService = getAudioService(); - mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); - mActivityManager = - (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); - mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); - mAudioPlayerStateMonitor.registerListener( - (config, isRemoved) -> { - if (isRemoved || !config.isActive() || config.getPlayerType() - == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { - return; - } - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked( - UserHandle.getUserId(config.getClientUid())); - if (user != null) { - user.mPriorityStack.updateMediaButtonSessionIfNeeded(); - } - } - }, null /* handler */); - mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); - mContentResolver = getContext().getContentResolver(); - mSettingsObserver = new SettingsObserver(); - mSettingsObserver.observe(); - mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_LEANBACK); - - updateUser(); - } - - private IAudioService getAudioService() { - IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); - return IAudioService.Stub.asInterface(b); - } - - private boolean isGlobalPriorityActiveLocked() { - return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); - } - - public void updateSession(MediaSessionRecord record) { - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - if (user == null) { - Log.w(TAG, "Unknown session updated. Ignoring."); - return; - } - if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Global priority session is updated, active=" + record.isActive()); - } - user.pushAddressedPlayerChangedLocked(); - } else { - if (!user.mPriorityStack.contains(record)) { - Log.w(TAG, "Unknown session updated. Ignoring."); - return; - } - user.mPriorityStack.onSessionStateChange(record); - } - mHandler.postSessionsChanged(record.getUserId()); - } - } - - public void setGlobalPrioritySession(MediaSessionRecord record) { - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - if (mGlobalPrioritySession != record) { - Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession - + " to " + record); - mGlobalPrioritySession = record; - if (user != null && user.mPriorityStack.contains(record)) { - // Handle the global priority session separately. - // Otherwise, it can be the media button session regardless of the active state - // because it or other system components might have been the lastly played media - // app. - user.mPriorityStack.removeSession(record); - } - } - } - } - - private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { - List<MediaSessionRecord> records = new ArrayList<>(); - if (userId == USER_ALL) { - int size = mUserRecords.size(); - for (int i = 0; i < size; i++) { - records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); - } - } else { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null) { - Log.w(TAG, "getSessions failed. Unknown user " + userId); - return records; - } - records.addAll(user.mPriorityStack.getActiveSessions(userId)); - } - // Return global priority session at the first whenever it's asked. - if (isGlobalPriorityActiveLocked() - && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) { - records.add(0, mGlobalPrioritySession); - } - return records; - } - - List<Session2Token> getSession2TokensLocked(int userId) { - List<Session2Token> list = new ArrayList<>(); - if (userId == USER_ALL) { - for (int i = 0; i < mSession2TokensPerUser.size(); i++) { - list.addAll(mSession2TokensPerUser.valueAt(i)); - } - } else { - list.addAll(mSession2TokensPerUser.get(userId)); - } - return list; - } - - /** - * Tells the system UI that volume has changed on an active remote session. - */ - public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { - if (mRvc == null || !session.isActive()) { - return; - } - try { - mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); - } catch (Exception e) { - Log.wtf(TAG, "Error sending volume change to system UI.", e); - } - } - - public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - if (user == null || !user.mPriorityStack.contains(record)) { - Log.d(TAG, "Unknown session changed playback state. Ignoring."); - return; - } - user.mPriorityStack.onPlaystateChanged(record, oldState, newState); - } - } - - public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - if (user == null || !user.mPriorityStack.contains(record)) { - Log.d(TAG, "Unknown session changed playback type. Ignoring."); - return; - } - pushRemoteVolumeUpdateLocked(record.getUserId()); - } + mImpl.onStart(); } @Override public void onStartUser(int userId) { - if (DEBUG) Log.d(TAG, "onStartUser: " + userId); - updateUser(); + mImpl.onStartUser(userId); } @Override public void onSwitchUser(int userId) { - if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId); - updateUser(); + mImpl.onSwitchUser(userId); } // Called when the user with the userId is removed. @Override public void onStopUser(int userId) { - if (DEBUG) Log.d(TAG, "onStopUser: " + userId); - synchronized (mLock) { - // TODO: Also handle removing user in updateUser() because adding/switching user is - // handled in updateUser(). - FullUserRecord user = getFullUserRecordLocked(userId); - if (user != null) { - if (user.mFullUserId == userId) { - user.destroySessionsForUserLocked(USER_ALL); - mUserRecords.remove(userId); - } else { - user.destroySessionsForUserLocked(userId); - } - } - mSession2TokensPerUser.remove(userId); - updateUser(); - } + mImpl.onStopUser(userId); } @Override public void monitor() { - synchronized (mLock) { - // Check for deadlock - } - } - - protected void enforcePhoneStatePermission(int pid, int uid) { - if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); - } + mImpl.monitor(); } - void sessionDied(MediaSessionRecord session) { - synchronized (mLock) { - destroySessionLocked(session); - } - } - - void destroySession(MediaSessionRecord session) { - synchronized (mLock) { - destroySessionLocked(session); - } - } - - private void updateUser() { - synchronized (mLock) { - UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); - mFullUserIds.clear(); - List<UserInfo> allUsers = manager.getUsers(); - if (allUsers != null) { - for (UserInfo userInfo : allUsers) { - if (userInfo.isManagedProfile()) { - mFullUserIds.put(userInfo.id, userInfo.profileGroupId); - } else { - mFullUserIds.put(userInfo.id, userInfo.id); - if (mUserRecords.get(userInfo.id) == null) { - mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); - } - } - if (mSession2TokensPerUser.get(userInfo.id) == null) { - mSession2TokensPerUser.put(userInfo.id, new ArrayList<>()); - } - } - } - // Ensure that the current full user exists. - int currentFullUserId = ActivityManager.getCurrentUser(); - mCurrentFullUserRecord = mUserRecords.get(currentFullUserId); - if (mCurrentFullUserRecord == null) { - Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); - mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); - mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); - if (mSession2TokensPerUser.get(currentFullUserId) == null) { - mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>()); - } - } - mFullUserIds.put(currentFullUserId, currentFullUserId); - } - } - - private void updateActiveSessionListeners() { - synchronized (mLock) { - for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { - SessionsListenerRecord listener = mSessionsListeners.get(i); - try { - enforceMediaPermissions(listener.componentName, listener.pid, listener.uid, - listener.userId); - } catch (SecurityException e) { - Log.i(TAG, "ActiveSessionsListener " + listener.componentName - + " is no longer authorized. Disconnecting."); - mSessionsListeners.remove(i); - try { - listener.listener - .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); - } catch (Exception e1) { - // ignore - } - } - } - } - } - - /* - * When a session is removed several things need to happen. - * 1. We need to remove it from the relevant user. - * 2. We need to remove it from the priority stack. - * 3. We need to remove it from all sessions. - * 4. If this is the system priority session we need to clear it. - * 5. We need to unlink to death from the cb binder - * 6. We need to tell the session to do any final cleanup (onDestroy) + /** + * Updates session. */ - private void destroySessionLocked(MediaSessionRecord session) { - if (DEBUG) { - Log.d(TAG, "Destroying " + session); - } - FullUserRecord user = getFullUserRecordLocked(session.getUserId()); - if (mGlobalPrioritySession == session) { - mGlobalPrioritySession = null; - if (session.isActive() && user != null) { - user.pushAddressedPlayerChangedLocked(); - } - } else { - if (user != null) { - user.mPriorityStack.removeSession(session); - } - } - - try { - session.getCallback().getBinder().unlinkToDeath(session, 0); - } catch (Exception e) { - // ignore exceptions while destroying a session. - } - session.onDestroy(); - mHandler.postSessionsChanged(session.getUserId()); - } - - private void enforcePackageName(String packageName, int uid) { - if (TextUtils.isEmpty(packageName)) { - throw new IllegalArgumentException("packageName may not be empty"); - } - String[] packages = getContext().getPackageManager().getPackagesForUid(uid); - final int packageCount = packages.length; - for (int i = 0; i < packageCount; i++) { - if (packageName.equals(packages[i])) { - return; - } - } - throw new IllegalArgumentException("packageName is not owned by the calling process"); + public void updateSession(MediaSessionRecord record) { + mImpl.updateSession(record); } /** - * Checks a caller's authorization to register an IRemoteControlDisplay. - * Authorization is granted if one of the following is true: - * <ul> - * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL - * permission</li> - * <li>the caller's listener is one of the enabled notification listeners - * for the caller's user</li> - * </ul> + * Sets global priority session. */ - private void enforceMediaPermissions(ComponentName compName, int pid, int uid, - int resolvedUserId) { - if (isCurrentVolumeController(pid, uid)) return; - if (getContext() - .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) - != PackageManager.PERMISSION_GRANTED - && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), - resolvedUserId)) { - throw new SecurityException("Missing permission to control media."); - } - } - - private boolean isCurrentVolumeController(int pid, int uid) { - return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, - pid, uid) == PackageManager.PERMISSION_GRANTED; + public void setGlobalPrioritySession(MediaSessionRecord record) { + mImpl.setGlobalPrioritySession(record); } - private void enforceSystemUiPermission(String action, int pid, int uid) { - if (!isCurrentVolumeController(pid, uid)) { - throw new SecurityException("Only system ui may " + action); - } + List<Session2Token> getSession2TokensLocked(int userId) { + return mImpl.getSession2TokensLocked(userId); } /** - * This checks if the component is an enabled notification listener for the - * specified user. Enabled components may only operate on behalf of the user - * they're running as. - * - * @param compName The component that is enabled. - * @param userId The user id of the caller. - * @param forUserId The user id they're making the request on behalf of. - * @return True if the component is enabled, false otherwise + * Tells the system UI that volume has changed on an active remote session. */ - private boolean isEnabledNotificationListener(ComponentName compName, int userId, - int forUserId) { - if (userId != forUserId) { - // You may not access another user's content as an enabled listener. - return false; - } - if (DEBUG) { - Log.d(TAG, "Checking if enabled notification listener " + compName); - } - if (compName != null) { - try { - return mNotificationManager.isNotificationListenerAccessGrantedForUser( - compName, userId); - } catch(RemoteException e) { - Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e); - } - } - return false; - } - - private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, - String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException { - synchronized (mLock) { - return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); - } + public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { + mImpl.notifyRemoteVolumeChanged(flags, session); } - /* - * When a session is created the following things need to happen. - * 1. Its callback binder needs a link to death - * 2. It needs to be added to all sessions. - * 3. It needs to be added to the priority stack. - * 4. It needs to be added to the relevant user record. + /** + * Called when session playstate is changed. */ - private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, - String callerPackageName, SessionCallbackLink cb, String tag) { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null) { - Log.wtf(TAG, "Request from invalid user: " + userId); - throw new RuntimeException("Session request from invalid user."); - } - - final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, - callerPackageName, cb, tag, this, mHandler.getLooper()); - try { - cb.getBinder().linkToDeath(session, 0); - } catch (RemoteException e) { - throw new RuntimeException("Media Session owner died prematurely.", e); - } - - user.mPriorityStack.addSession(session); - mHandler.postSessionsChanged(userId); - - if (DEBUG) { - Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); - } - return session; + public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { + mImpl.onSessionPlaystateChanged(record, oldState, newState); } - private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { - for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { - if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) { - return i; - } - } - return -1; + /** + * Called when session playback type is changed. + */ + public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { + mImpl.onSessionPlaybackTypeChanged(record); } - private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) { - for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { - if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) { - return i; - } - } - return -1; + protected void enforcePhoneStatePermission(int pid, int uid) { + mImpl.enforcePhoneStatePermission(pid, uid); } - - private void pushSessionsChanged(int userId) { - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null) { - Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); - return; - } - List<MediaSessionRecord> records = getActiveSessionsLocked(userId); - int size = records.size(); - ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); - for (int i = 0; i < size; i++) { - tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); - } - pushRemoteVolumeUpdateLocked(userId); - for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { - SessionsListenerRecord record = mSessionsListeners.get(i); - if (record.userId == USER_ALL || record.userId == userId) { - try { - record.listener.onActiveSessionsChanged(tokens); - } catch (RemoteException e) { - Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", - e); - mSessionsListeners.remove(i); - } - } - } - } + void sessionDied(MediaSessionRecord session) { + mImpl.sessionDied(session); } - private void pushRemoteVolumeUpdateLocked(int userId) { - if (mRvc != null) { - try { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null) { - Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId); - return; - } - MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); - mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); - } - } + void destroySession(MediaSessionRecord session) { + mImpl.destroySession(session); } void pushSession2TokensChangedLocked(int userId) { - List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL); - List<Session2Token> session2Tokens = getSession2TokensLocked(userId); - - for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { - Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); - try { - if (listenerRecord.userId == USER_ALL) { - listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); - } else if (listenerRecord.userId == userId) { - listenerRecord.listener.onSession2TokensChanged(session2Tokens); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); - mSession2TokensListenerRecords.remove(i); - } - } + mImpl.pushSession2TokensChangedLocked(userId); } /** - * Called when the media button receiver for the {@param record} is changed. - * - * @param record the media session whose media button receiver is updated. + * Called when media button receiver changed. */ public void onMediaButtonReceiverChanged(MediaSessionRecord record) { - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - MediaSessionRecord mediaButtonSession = - user.mPriorityStack.getMediaButtonSession(); - if (record == mediaButtonSession) { - user.rememberMediaButtonReceiverLocked(mediaButtonSession); - } - } - } - - private String getCallingPackageName(int uid) { - String[] packages = getContext().getPackageManager().getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - return packages[0]; - } - return ""; - } - - private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) { - if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { - return; - } - try { - mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); - } catch (RemoteException e) { - Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener"); - } - } - - private FullUserRecord getFullUserRecordLocked(int userId) { - int fullUserId = mFullUserIds.get(userId, -1); - if (fullUserId < 0) { - return null; - } - return mUserRecords.get(fullUserId); - } - - /** - * Information about a full user and its corresponding managed profiles. - * - * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate - * them when he/she presses a media/volume button. So keeping media sessions for them in one - * place makes more sense and increases the readability.</p> - * <p>The contents of this object is guarded by {@link #mLock}. - */ - final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener { - public static final int COMPONENT_TYPE_INVALID = 0; - public static final int COMPONENT_TYPE_BROADCAST = 1; - public static final int COMPONENT_TYPE_ACTIVITY = 2; - public static final int COMPONENT_TYPE_SERVICE = 3; - private static final String COMPONENT_NAME_USER_ID_DELIM = ","; - - private final int mFullUserId; - private final MediaSessionStack mPriorityStack; - private PendingIntent mLastMediaButtonReceiver; - private ComponentName mRestoredMediaButtonReceiver; - private int mRestoredMediaButtonReceiverComponentType; - private int mRestoredMediaButtonReceiverUserId; - - private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener; - private int mOnVolumeKeyLongPressListenerUid; - private KeyEvent mInitialDownVolumeKeyEvent; - private int mInitialDownVolumeStream; - private boolean mInitialDownMusicOnly; - - private IOnMediaKeyListener mOnMediaKeyListener; - private int mOnMediaKeyListenerUid; - private ICallback mCallback; - - public FullUserRecord(int fullUserId) { - mFullUserId = fullUserId; - mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); - // Restore the remembered media button receiver before the boot. - String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver, - Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); - if (mediaButtonReceiverInfo == null) { - return; - } - String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM); - if (tokens == null || (tokens.length != 2 && tokens.length != 3)) { - return; - } - mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]); - mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]); - if (tokens.length == 3) { - mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]); - } else { - mRestoredMediaButtonReceiverComponentType = - getComponentType(mRestoredMediaButtonReceiver); - } - } - - public void destroySessionsForUserLocked(int userId) { - List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId); - for (MediaSessionRecord session : sessions) { - MediaSessionService.this.destroySessionLocked(session); - } - } - - public void dumpLocked(PrintWriter pw, String prefix) { - pw.print(prefix + "Record for full_user=" + mFullUserId); - // Dump managed profile user ids associated with this user. - int size = mFullUserIds.size(); - for (int i = 0; i < size; i++) { - if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i) - && mFullUserIds.valueAt(i) == mFullUserId) { - pw.print(", profile_user=" + mFullUserIds.keyAt(i)); - } - } - pw.println(); - String indent = prefix + " "; - pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener); - pw.println(indent + "Volume key long-press listener package: " + - getCallingPackageName(mOnVolumeKeyLongPressListenerUid)); - pw.println(indent + "Media key listener: " + mOnMediaKeyListener); - pw.println(indent + "Media key listener package: " + - getCallingPackageName(mOnMediaKeyListenerUid)); - pw.println(indent + "Callback: " + mCallback); - pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver); - pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver); - pw.println(indent + "Restored MediaButtonReceiverComponentType: " - + mRestoredMediaButtonReceiverComponentType); - mPriorityStack.dump(pw, indent); - pw.println(indent + "Session2Tokens:"); - for (int i = 0; i < mSession2TokensPerUser.size(); i++) { - List<Session2Token> list = mSession2TokensPerUser.valueAt(i); - if (list == null || list.size() == 0) { - continue; - } - for (Session2Token token : list) { - pw.println(indent + " " + token); - } - } - } - - @Override - public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, - MediaSessionRecord newMediaButtonSession) { - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); - } - synchronized (mLock) { - if (oldMediaButtonSession != null) { - mHandler.postSessionsChanged(oldMediaButtonSession.getUserId()); - } - if (newMediaButtonSession != null) { - rememberMediaButtonReceiverLocked(newMediaButtonSession); - mHandler.postSessionsChanged(newMediaButtonSession.getUserId()); - } - pushAddressedPlayerChangedLocked(); - } - } - - // Remember media button receiver and keep it in the persistent storage. - public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { - PendingIntent receiver = record.getMediaButtonReceiver(); - mLastMediaButtonReceiver = receiver; - mRestoredMediaButtonReceiver = null; - mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID; - - String mediaButtonReceiverInfo = ""; - if (receiver != null) { - ComponentName component = receiver.getIntent().getComponent(); - if (component != null - && record.getPackageName().equals(component.getPackageName())) { - String componentName = component.flattenToString(); - int componentType = getComponentType(component); - mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM, - componentName, String.valueOf(record.getUserId()), - String.valueOf(componentType)); - } - } - Settings.Secure.putStringForUser(mContentResolver, - Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo, - mFullUserId); - } - - private void pushAddressedPlayerChangedLocked() { - if (mCallback == null) { - return; - } - try { - MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); - if (mediaButtonSession != null) { - mCallback.onAddressedPlayerChangedToMediaSession( - new MediaSession.Token(mediaButtonSession.getControllerBinder())); - } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { - mCallback.onAddressedPlayerChangedToMediaButtonReceiver( - mCurrentFullUserRecord.mLastMediaButtonReceiver - .getIntent().getComponent()); - } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { - mCallback.onAddressedPlayerChangedToMediaButtonReceiver( - mCurrentFullUserRecord.mRestoredMediaButtonReceiver); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); - } - } - - private MediaSessionRecord getMediaButtonSessionLocked() { - return isGlobalPriorityActiveLocked() - ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); - } - - private int getComponentType(@Nullable ComponentName componentName) { - if (componentName == null) { - return COMPONENT_TYPE_INVALID; - } - PackageManager pm = getContext().getPackageManager(); - try { - ActivityInfo activityInfo = pm.getActivityInfo(componentName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.GET_ACTIVITIES); - if (activityInfo != null) { - return COMPONENT_TYPE_ACTIVITY; - } - } catch (NameNotFoundException e) { - } - try { - ServiceInfo serviceInfo = pm.getServiceInfo(componentName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.GET_SERVICES); - if (serviceInfo != null) { - return COMPONENT_TYPE_SERVICE; - } - } catch (NameNotFoundException e) { - } - // Pick legacy behavior for BroadcastReceiver or unknown. - return COMPONENT_TYPE_BROADCAST; - } - } - - final class SessionsListenerRecord implements IBinder.DeathRecipient { - public final IActiveSessionsListener listener; - public final ComponentName componentName; - public final int userId; - public final int pid; - public final int uid; - - public SessionsListenerRecord(IActiveSessionsListener listener, - ComponentName componentName, - int userId, int pid, int uid) { - this.listener = listener; - this.componentName = componentName; - this.userId = userId; - this.pid = pid; - this.uid = uid; - } - - @Override - public void binderDied() { - synchronized (mLock) { - mSessionsListeners.remove(this); - } - } - } - - final class Session2TokensListenerRecord implements IBinder.DeathRecipient { - public final ISession2TokensListener listener; - public final int userId; - - Session2TokensListenerRecord(ISession2TokensListener listener, - int userId) { - this.listener = listener; - this.userId = userId; - } - - @Override - public void binderDied() { - synchronized (mLock) { - mSession2TokensListenerRecords.remove(this); - } - } - } - - final class SettingsObserver extends ContentObserver { - private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); - - private SettingsObserver() { - super(null); - } - - private void observe() { - mContentResolver.registerContentObserver(mSecureSettingsUri, - false, this, USER_ALL); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - updateActiveSessionListeners(); - } - } - - class SessionManagerImpl extends ISessionManager.Stub { - private static final String EXTRA_WAKELOCK_ACQUIRED = - "android.media.AudioService.WAKELOCK_ACQUIRED"; - private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number - - private boolean mVoiceButtonDown = false; - private boolean mVoiceButtonHandled = false; - - @Override - public ISession createSession(String packageName, SessionCallbackLink cb, String tag, - int userId) throws RemoteException { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - enforcePackageName(packageName, uid); - int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, - false /* allowAll */, true /* requireFull */, "createSession", packageName); - if (cb == null) { - throw new IllegalArgumentException("Controller callback cannot be null"); - } - return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) - .getSessionBinder(); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void notifySession2Created(Session2Token sessionToken) throws RemoteException { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - if (DEBUG) { - Log.d(TAG, "Session2 is created " + sessionToken); - } - if (uid != sessionToken.getUid()) { - throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid - + " but actually=" + sessionToken.getUid()); - } - Controller2Callback callback = new Controller2Callback(sessionToken); - // Note: It's safe not to keep controller here because it wouldn't be GC'ed until - // it's closed. - // TODO: Keep controller as well for better readability - // because the GC behavior isn't straightforward. - MediaController2 controller = new MediaController2(getContext(), sessionToken, - new HandlerExecutor(mHandler), callback); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public List<IBinder> getSessions(ComponentName componentName, int userId) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); - ArrayList<IBinder> binders = new ArrayList<IBinder>(); - synchronized (mLock) { - List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId); - for (MediaSessionRecord record : records) { - binders.add(record.getControllerBinder().asBinder()); - } - } - return binders; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public List<Session2Token> getSession2Tokens(int userId) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - // Check that they can make calls on behalf of the user and - // get the final user id - int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, - true /* allowAll */, true /* requireFull */, "getSession2Tokens", - null /* optional packageName */); - List<Session2Token> result; - synchronized (mLock) { - result = getSession2TokensLocked(resolvedUserId); - } - return result; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void addSessionsListener(IActiveSessionsListener listener, - ComponentName componentName, int userId) throws RemoteException { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); - synchronized (mLock) { - int index = findIndexOfSessionsListenerLocked(listener); - if (index != -1) { - Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); - return; - } - SessionsListenerRecord record = new SessionsListenerRecord(listener, - componentName, resolvedUserId, pid, uid); - try { - listener.asBinder().linkToDeath(record, 0); - } catch (RemoteException e) { - Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); - return; - } - mSessionsListeners.add(record); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void removeSessionsListener(IActiveSessionsListener listener) - throws RemoteException { - synchronized (mLock) { - int index = findIndexOfSessionsListenerLocked(listener); - if (index != -1) { - SessionsListenerRecord record = mSessionsListeners.remove(index); - try { - record.listener.asBinder().unlinkToDeath(record, 0); - } catch (Exception e) { - // ignore exceptions, the record is being removed - } - } - } - } - - @Override - public void addSession2TokensListener(ISession2TokensListener listener, - int userId) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - // Check that they can make calls on behalf of the user and get the final user id. - int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, - true /* allowAll */, true /* requireFull */, "addSession2TokensListener", - null /* optional packageName */); - synchronized (mLock) { - int index = findIndexOfSession2TokensListenerLocked(listener); - if (index >= 0) { - Log.w(TAG, "addSession2TokensListener is already added, ignoring"); - return; - } - mSession2TokensListenerRecords.add( - new Session2TokensListenerRecord(listener, resolvedUserId)); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void removeSession2TokensListener(ISession2TokensListener listener) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - synchronized (mLock) { - int index = findIndexOfSession2TokensListenerLocked(listener); - if (index >= 0) { - Session2TokensListenerRecord listenerRecord = - mSession2TokensListenerRecords.remove(index); - try { - listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0); - } catch (Exception e) { - // Ignore exception. - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Handles the dispatching of the media button events to one of the - * registered listeners, or if there was none, broadcast an - * ACTION_MEDIA_BUTTON intent to the rest of the system. - * - * @param packageName The caller package - * @param asSystemService {@code true} if the event sent to the session as if it was come - * from the system service instead of the app process. This helps sessions to - * distinguish between the key injection by the app and key events from the - * hardware devices. Should be used only when the volume key events aren't handled - * by foreground activity. {@code false} otherwise to tell session about the real - * caller. - * @param keyEvent a non-null KeyEvent whose key code is one of the - * supported media buttons - * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held - * while this key event is dispatched. - */ - @Override - public void dispatchMediaKeyEvent(String packageName, boolean asSystemService, - KeyEvent keyEvent, boolean needWakeLock) { - if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { - Log.w(TAG, "Attempted to dispatch null or non-media key event."); - return; - } - - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - if (DEBUG) { - Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid - + ", uid=" + uid + ", asSystem=" + asSystemService + ", event=" - + keyEvent); - } - if (!isUserSetupComplete()) { - // Global media key handling can have the side-effect of starting new - // activities which is undesirable while setup is in progress. - Slog.i(TAG, "Not dispatching media key event because user " - + "setup is in progress."); - return; - } - - synchronized (mLock) { - boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); - if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { - // Prevent dispatching key event through reflection while the global - // priority session is active. - Slog.i(TAG, "Only the system can dispatch media key event " - + "to the global priority session."); - return; - } - if (!isGlobalPriorityActive) { - if (mCurrentFullUserRecord.mOnMediaKeyListener != null) { - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Send " + keyEvent + " to the media key listener"); - } - try { - mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, - new MediaKeyListenerResultReceiver(packageName, pid, uid, - asSystemService, keyEvent, needWakeLock)); - return; - } catch (RemoteException e) { - Log.w(TAG, "Failed to send " + keyEvent - + " to the media key listener"); - } - } - } - if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) { - handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, - needWakeLock); - } else { - dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, - keyEvent, needWakeLock); - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void setCallback(ICallback callback) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) { - throw new SecurityException("Only Bluetooth service processes can set" - + " Callback"); - } - synchronized (mLock) { - int userId = UserHandle.getUserId(uid); - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null || user.mFullUserId != userId) { - Log.w(TAG, "Only the full user can set the callback" - + ", userId=" + userId); - return; - } - user.mCallback = callback; - Log.d(TAG, "The callback " + user.mCallback - + " is set by " + getCallingPackageName(uid)); - if (user.mCallback == null) { - return; - } - try { - user.mCallback.asBinder().linkToDeath( - new IBinder.DeathRecipient() { - @Override - public void binderDied() { - synchronized (mLock) { - user.mCallback = null; - } - } - }, 0); - user.pushAddressedPlayerChangedLocked(); - } catch (RemoteException e) { - Log.w(TAG, "Failed to set callback", e); - user.mCallback = null; - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission. - if (getContext().checkPermission( - android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" + - " permission."); - } - - synchronized (mLock) { - int userId = UserHandle.getUserId(uid); - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null || user.mFullUserId != userId) { - Log.w(TAG, "Only the full user can set the volume key long-press listener" - + ", userId=" + userId); - return; - } - if (user.mOnVolumeKeyLongPressListener != null && - user.mOnVolumeKeyLongPressListenerUid != uid) { - Log.w(TAG, "The volume key long-press listener cannot be reset" - + " by another app , mOnVolumeKeyLongPressListener=" - + user.mOnVolumeKeyLongPressListenerUid - + ", uid=" + uid); - return; - } - - user.mOnVolumeKeyLongPressListener = listener; - user.mOnVolumeKeyLongPressListenerUid = uid; - - Log.d(TAG, "The volume key long-press listener " - + listener + " is set by " + getCallingPackageName(uid)); - - if (user.mOnVolumeKeyLongPressListener != null) { - try { - user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath( - new IBinder.DeathRecipient() { - @Override - public void binderDied() { - synchronized (mLock) { - user.mOnVolumeKeyLongPressListener = null; - } - } - }, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failed to set death recipient " - + user.mOnVolumeKeyLongPressListener); - user.mOnVolumeKeyLongPressListener = null; - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void setOnMediaKeyListener(IOnMediaKeyListener listener) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - // Enforce SET_MEDIA_KEY_LISTENER permission. - if (getContext().checkPermission( - android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" + - " permission."); - } - - synchronized (mLock) { - int userId = UserHandle.getUserId(uid); - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null || user.mFullUserId != userId) { - Log.w(TAG, "Only the full user can set the media key listener" - + ", userId=" + userId); - return; - } - if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) { - Log.w(TAG, "The media key listener cannot be reset by another app. " - + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid - + ", uid=" + uid); - return; - } - - user.mOnMediaKeyListener = listener; - user.mOnMediaKeyListenerUid = uid; - - Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener - + " is set by " + getCallingPackageName(uid)); - - if (user.mOnMediaKeyListener != null) { - try { - user.mOnMediaKeyListener.asBinder().linkToDeath( - new IBinder.DeathRecipient() { - @Override - public void binderDied() { - synchronized (mLock) { - user.mOnMediaKeyListener = null; - } - } - }, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener); - user.mOnMediaKeyListener = null; - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Handles the dispatching of the volume button events to one of the - * registered listeners. If there's a volume key long-press listener and - * there's no active global priority session, long-pressess will be sent to the - * long-press listener instead of adjusting volume. - * - * @param packageName The caller's package name, obtained by Context#getPackageName() - * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName() - * @param asSystemService {@code true} if the event sent to the session as if it was come - * from the system service instead of the app process. This helps sessions to - * distinguish between the key injection by the app and key events from the - * hardware devices. Should be used only when the volume key events aren't handled - * by foreground activity. {@code false} otherwise to tell session about the real - * caller. - * @param keyEvent a non-null KeyEvent whose key code is one of the - * {@link KeyEvent#KEYCODE_VOLUME_UP}, - * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, - * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. - * @param stream stream type to adjust volume. - * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume. - */ - @Override - public void dispatchVolumeKeyEvent(String packageName, String opPackageName, - boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) { - if (keyEvent == null || - (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP - && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN - && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) { - Log.w(TAG, "Attempted to dispatch null or non-volume key event."); - return; - } - - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid=" - + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent); - } - - try { - synchronized (mLock) { - if (isGlobalPriorityActiveLocked() - || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { - dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, - asSystemService, keyEvent, stream, musicOnly); - } else { - // TODO: Consider the case when both volume up and down keys are pressed - // at the same time. - if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { - if (keyEvent.getRepeatCount() == 0) { - // Keeps the copy of the KeyEvent because it can be reused. - mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = - KeyEvent.obtain(keyEvent); - mCurrentFullUserRecord.mInitialDownVolumeStream = stream; - mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly; - mHandler.sendMessageDelayed( - mHandler.obtainMessage( - MessageHandler.MSG_VOLUME_INITIAL_DOWN, - mCurrentFullUserRecord.mFullUserId, 0), - mLongPressTimeout); - } - if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { - mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); - if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) { - dispatchVolumeKeyLongPressLocked( - mCurrentFullUserRecord.mInitialDownVolumeKeyEvent); - // Mark that the key is already handled. - mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null; - } - dispatchVolumeKeyLongPressLocked(keyEvent); - } - } else { // if up - mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); - if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null - && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent - .getDownTime() == keyEvent.getDownTime()) { - // Short-press. Should change volume. - dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, - asSystemService, - mCurrentFullUserRecord.mInitialDownVolumeKeyEvent, - mCurrentFullUserRecord.mInitialDownVolumeStream, - mCurrentFullUserRecord.mInitialDownMusicOnly); - dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, - asSystemService, keyEvent, stream, musicOnly); - } else { - dispatchVolumeKeyLongPressLocked(keyEvent); - } - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid, - int uid, boolean asSystemService, KeyEvent keyEvent, int stream, - boolean musicOnly) { - boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; - boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; - int direction = 0; - boolean isMute = false; - switch (keyEvent.getKeyCode()) { - case KeyEvent.KEYCODE_VOLUME_UP: - direction = AudioManager.ADJUST_RAISE; - break; - case KeyEvent.KEYCODE_VOLUME_DOWN: - direction = AudioManager.ADJUST_LOWER; - break; - case KeyEvent.KEYCODE_VOLUME_MUTE: - isMute = true; - break; - } - if (down || up) { - int flags = AudioManager.FLAG_FROM_KEY; - if (musicOnly) { - // This flag is used when the screen is off to only affect active media. - flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY; - } else { - // These flags are consistent with the home screen - if (up) { - flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; - } else { - flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; - } - } - if (direction != 0) { - // If this is action up we want to send a beep for non-music events - if (up) { - direction = 0; - } - dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, - asSystemService, stream, direction, flags); - } else if (isMute) { - if (down && keyEvent.getRepeatCount() == 0) { - dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, - asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags); - } - } - } - } - - @Override - public void dispatchAdjustVolume(String packageName, String opPackageName, - int suggestedStream, int delta, int flags) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false, - suggestedStream, delta, flags); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void setRemoteVolumeController(IRemoteVolumeController rvc) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - enforceSystemUiPermission("listen for volume changes", pid, uid); - mRvc = rvc; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public boolean isGlobalPriorityActive() { - synchronized (mLock) { - return isGlobalPriorityActiveLocked(); - } - } - - @Override - public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; - - pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); - pw.println(); - - synchronized (mLock) { - pw.println(mSessionsListeners.size() + " sessions listeners."); - pw.println("Global priority session is " + mGlobalPrioritySession); - if (mGlobalPrioritySession != null) { - mGlobalPrioritySession.dump(pw, " "); - } - pw.println("User Records:"); - int count = mUserRecords.size(); - for (int i = 0; i < count; i++) { - mUserRecords.valueAt(i).dumpLocked(pw, ""); - } - mAudioPlayerStateMonitor.dump(getContext(), pw, ""); - } - } - - /** - * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL - * permission or an enabled notification listener) - * - * @param controllerPackageName package name of the controller app - * @param controllerPid pid of the controller app - * @param controllerUid uid of the controller app - */ - @Override - public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid) - throws RemoteException { - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - // Don't perform sanity check between controllerPackageName and controllerUid. - // When an (activity|service) runs on the another apps process by specifying - // android:process in the AndroidManifest.xml, then PID and UID would have the - // running process' information instead of the (activity|service) that has created - // MediaController. - // Note that we can use Context#getOpPackageName() instead of - // Context#getPackageName() for getting package name that matches with the PID/UID, - // but it doesn't tell which package has created the MediaController, so useless. - return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName, - controllerPid, controllerUid); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - // For MediaSession - private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, - final int uid) { - String packageName = null; - if (componentName != null) { - // If they gave us a component name verify they own the - // package - packageName = componentName.getPackageName(); - enforcePackageName(packageName, uid); - } - // Check that they can make calls on behalf of the user and - // get the final user id - int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, - true /* allowAll */, true /* requireFull */, "getSessions", packageName); - // Check if they have the permissions or their component is - // enabled for the user they're calling from. - enforceMediaPermissions(componentName, pid, uid, resolvedUserId); - return resolvedUserId; - } - - private boolean hasMediaControlPermission(int resolvedUserId, String packageName, - int pid, int uid) throws RemoteException { - // Allow API calls from the System UI - if (isCurrentVolumeController(pid, uid)) { - return true; - } - - // Check if it's system server or has MEDIA_CONTENT_CONTROL. - // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra - // check here. - if (uid == Process.SYSTEM_UID || getContext().checkPermission( - android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) - == PackageManager.PERMISSION_GRANTED) { - return true; - } else if (DEBUG) { - Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); - } - - // You may not access another user's content as an enabled listener. - final int userId = UserHandle.getUserId(uid); - if (resolvedUserId != userId) { - return false; - } - - // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( - // String pkgName) to notification team for optimization - final List<ComponentName> enabledNotificationListeners = - mNotificationManager.getEnabledNotificationListeners(userId); - if (enabledNotificationListeners != null) { - for (int i = 0; i < enabledNotificationListeners.size(); i++) { - if (TextUtils.equals(packageName, - enabledNotificationListeners.get(i).getPackageName())) { - return true; - } - } - } - if (DEBUG) { - Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " - + "notification listener"); - } - return false; - } - - private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid, - int uid, boolean asSystemService, int suggestedStream, int direction, int flags) { - MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession - : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); - - boolean preferSuggestedStream = false; - if (isValidLocalStreamType(suggestedStream) - && AudioSystem.isStreamActive(suggestedStream, 0)) { - preferSuggestedStream = true; - } - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags=" - + flags + ", suggestedStream=" + suggestedStream - + ", preferSuggestedStream=" + preferSuggestedStream); - } - if (session == null || preferSuggestedStream) { - if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0 - && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { - if (DEBUG) { - Log.d(TAG, "No active session to adjust, skipping media only volume event"); - } - return; - } - - // Execute mAudioService.adjustSuggestedStreamVolume() on - // handler thread of MediaSessionService. - // This will release the MediaSessionService.mLock sooner and avoid - // a potential deadlock between MediaSessionService.mLock and - // ActivityManagerService lock. - mHandler.post(new Runnable() { - @Override - public void run() { - final String callingOpPackageName; - final int callingUid; - if (asSystemService) { - callingOpPackageName = getContext().getOpPackageName(); - callingUid = Process.myUid(); - } else { - callingOpPackageName = opPackageName; - callingUid = uid; - } - try { - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream, - direction, flags, callingOpPackageName, callingUid); - } catch (SecurityException | IllegalArgumentException e) { - Log.e(TAG, "Cannot adjust volume: direction=" + direction - + ", suggestedStream=" + suggestedStream + ", flags=" + flags - + ", packageName=" + packageName + ", uid=" + uid - + ", asSystemService=" + asSystemService, e); - } - } - }); - } else { - session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService, - direction, flags, true); - } - } - - private void handleVoiceKeyEventLocked(String packageName, int pid, int uid, - boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - int action = keyEvent.getAction(); - boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; - if (action == KeyEvent.ACTION_DOWN) { - if (keyEvent.getRepeatCount() == 0) { - mVoiceButtonDown = true; - mVoiceButtonHandled = false; - } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) { - mVoiceButtonHandled = true; - startVoiceInput(needWakeLock); - } - } else if (action == KeyEvent.ACTION_UP) { - if (mVoiceButtonDown) { - mVoiceButtonDown = false; - if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { - // Resend the down then send this event through - KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); - dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, - downEvent, needWakeLock); - dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, - keyEvent, needWakeLock); - } - } - } - } - - private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, - boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); - if (session != null) { - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Sending " + keyEvent + " to " + session); - } - if (needWakeLock) { - mKeyEventReceiver.aquireWakeLockLocked(); - } - // If we don't need a wakelock use -1 as the id so we won't release it later. - session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent, - needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, - mKeyEventReceiver); - if (mCurrentFullUserRecord.mCallback != null) { - try { - mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession( - keyEvent, new MediaSession.Token(session.getControllerBinder())); - } catch (RemoteException e) { - Log.w(TAG, "Failed to send callback", e); - } - } - } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null - || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { - if (needWakeLock) { - mKeyEventReceiver.aquireWakeLockLocked(); - } - Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); - // TODO: Find a way to also send PID/UID in secure way. - String callerPackageName = - (asSystemService) ? getContext().getPackageName() : packageName; - mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName); - try { - if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { - PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver; - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Sending " + keyEvent - + " to the last known PendingIntent " + receiver); - } - receiver.send(getContext(), - needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, - mediaButtonIntent, mKeyEventReceiver, mHandler); - if (mCurrentFullUserRecord.mCallback != null) { - ComponentName componentName = mCurrentFullUserRecord - .mLastMediaButtonReceiver.getIntent().getComponent(); - if (componentName != null) { - mCurrentFullUserRecord.mCallback - .onMediaKeyEventDispatchedToMediaButtonReceiver( - keyEvent, componentName); - } - } - } else { - ComponentName receiver = - mCurrentFullUserRecord.mRestoredMediaButtonReceiver; - int componentType = mCurrentFullUserRecord - .mRestoredMediaButtonReceiverComponentType; - UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord - .mRestoredMediaButtonReceiverUserId); - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Sending " + keyEvent + " to the restored intent " - + receiver + ", type=" + componentType); - } - mediaButtonIntent.setComponent(receiver); - try { - switch (componentType) { - case FullUserRecord.COMPONENT_TYPE_ACTIVITY: - getContext().startActivityAsUser(mediaButtonIntent, userHandle); - break; - case FullUserRecord.COMPONENT_TYPE_SERVICE: - getContext().startForegroundServiceAsUser(mediaButtonIntent, - userHandle); - break; - default: - // Legacy behavior for other cases. - getContext().sendBroadcastAsUser(mediaButtonIntent, userHandle); - } - } catch (Exception e) { - Log.w(TAG, "Error sending media button to the restored intent " - + receiver + ", type=" + componentType, e); - } - if (mCurrentFullUserRecord.mCallback != null) { - mCurrentFullUserRecord.mCallback - .onMediaKeyEventDispatchedToMediaButtonReceiver( - keyEvent, receiver); - } - } - } catch (CanceledException e) { - Log.i(TAG, "Error sending key event to media button receiver " - + mCurrentFullUserRecord.mLastMediaButtonReceiver, e); - } catch (RemoteException e) { - Log.w(TAG, "Failed to send callback", e); - } - } - } - - private void startVoiceInput(boolean needWakeLock) { - Intent voiceIntent = null; - // select which type of search to launch: - // - screen on and device unlocked: action is ACTION_WEB_SEARCH - // - device locked or screen off: action is - // ACTION_VOICE_SEARCH_HANDS_FREE - // with EXTRA_SECURE set to true if the device is securely locked - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); - if (!isLocked && pm.isScreenOn()) { - voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); - Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); - } else { - voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); - voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, - isLocked && mKeyguardManager.isKeyguardSecure()); - Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); - } - // start the search activity - if (needWakeLock) { - mMediaEventWakeLock.acquire(); - } - try { - if (voiceIntent != null) { - voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent); - getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT); - } - } catch (ActivityNotFoundException e) { - Log.w(TAG, "No activity for search: " + e); - } finally { - if (needWakeLock) { - mMediaEventWakeLock.release(); - } - } - } - - private boolean isVoiceKey(int keyCode) { - return keyCode == KeyEvent.KEYCODE_HEADSETHOOK - || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); - } - - private boolean isUserSetupComplete() { - return Settings.Secure.getIntForUser(getContext().getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; - } - - // we only handle public stream types, which are 0-5 - private boolean isValidLocalStreamType(int streamType) { - return streamType >= AudioManager.STREAM_VOICE_CALL - && streamType <= AudioManager.STREAM_NOTIFICATION; - } - - private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { - private final String mPackageName; - private final int mPid; - private final int mUid; - private final boolean mAsSystemService; - private final KeyEvent mKeyEvent; - private final boolean mNeedWakeLock; - private boolean mHandled; - - private MediaKeyListenerResultReceiver(String packageName, int pid, int uid, - boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - super(mHandler); - mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT); - mPackageName = packageName; - mPid = pid; - mUid = uid; - mAsSystemService = asSystemService; - mKeyEvent = keyEvent; - mNeedWakeLock = needWakeLock; - } - - @Override - public void run() { - Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent); - dispatchMediaKeyEvent(); - } - - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) { - mHandled = true; - mHandler.removeCallbacks(this); - return; - } - dispatchMediaKeyEvent(); - } - - private void dispatchMediaKeyEvent() { - if (mHandled) { - return; - } - mHandled = true; - mHandler.removeCallbacks(this); - synchronized (mLock) { - if (!isGlobalPriorityActiveLocked() - && isVoiceKey(mKeyEvent.getKeyCode())) { - handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, - mKeyEvent, mNeedWakeLock); - } else { - dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, - mKeyEvent, mNeedWakeLock); - } - } - } - } - - private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); - - class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, - PendingIntent.OnFinished { - private final Handler mHandler; - private int mRefCount = 0; - private int mLastTimeoutId = 0; - - public KeyEventWakeLockReceiver(Handler handler) { - super(handler); - mHandler = handler; - } - - public void onTimeout() { - synchronized (mLock) { - if (mRefCount == 0) { - // We've already released it, so just return - return; - } - mLastTimeoutId++; - mRefCount = 0; - releaseWakeLockLocked(); - } - } - - public void aquireWakeLockLocked() { - if (mRefCount == 0) { - mMediaEventWakeLock.acquire(); - } - mRefCount++; - mHandler.removeCallbacks(this); - mHandler.postDelayed(this, WAKELOCK_TIMEOUT); - - } - - @Override - public void run() { - onTimeout(); - } - - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode < mLastTimeoutId) { - // Ignore results from calls that were before the last - // timeout, just in case. - return; - } else { - synchronized (mLock) { - if (mRefCount > 0) { - mRefCount--; - if (mRefCount == 0) { - releaseWakeLockLocked(); - } - } - } - } - } - - private void releaseWakeLockLocked() { - mMediaEventWakeLock.release(); - mHandler.removeCallbacks(this); - } - - @Override - public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, - String resultData, Bundle resultExtras) { - onReceiveResult(resultCode, null); - } - }; - - BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent == null) { - return; - } - Bundle extras = intent.getExtras(); - if (extras == null) { - return; - } - synchronized (mLock) { - if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) - && mMediaEventWakeLock.isHeld()) { - mMediaEventWakeLock.release(); - } - } - } - }; - } - - final class MessageHandler extends Handler { - private static final int MSG_SESSIONS_CHANGED = 1; - private static final int MSG_VOLUME_INITIAL_DOWN = 2; - private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_SESSIONS_CHANGED: - pushSessionsChanged((int) msg.obj); - break; - case MSG_VOLUME_INITIAL_DOWN: - synchronized (mLock) { - FullUserRecord user = mUserRecords.get((int) msg.arg1); - if (user != null && user.mInitialDownVolumeKeyEvent != null) { - dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent); - // Mark that the key is already handled. - user.mInitialDownVolumeKeyEvent = null; - } - } - break; - } - } - - public void postSessionsChanged(int userId) { - // Use object instead of the arguments when posting message to remove pending requests. - Integer userIdInteger = mIntegerCache.get(userId); - if (userIdInteger == null) { - userIdInteger = Integer.valueOf(userId); - mIntegerCache.put(userId, userIdInteger); - } - removeMessages(MSG_SESSIONS_CHANGED, userIdInteger); - obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget(); - } - } - - private class Controller2Callback extends MediaController2.ControllerCallback { - private final Session2Token mToken; - - Controller2Callback(Session2Token token) { - mToken = token; - } - - @Override - public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mToken.getUid()); - mSession2TokensPerUser.get(userId).add(mToken); - pushSession2TokensChangedLocked(userId); - } - } - - @Override - public void onDisconnected(MediaController2 controller) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mToken.getUid()); - mSession2TokensPerUser.get(userId).remove(mToken); - pushSession2TokensChangedLocked(userId); - } - } + mImpl.onMediaButtonReceiverChanged(record); + } + + abstract static class ServiceImpl { + public abstract void onStart(); + public abstract void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session); + public abstract void onSessionPlaystateChanged( + MediaSessionRecord record, int oldState, int newState); + public abstract void onSessionPlaybackTypeChanged(MediaSessionRecord record); + public abstract void onStartUser(int userId); + public abstract void onSwitchUser(int userId); + public abstract void monitor(); + public abstract void onMediaButtonReceiverChanged(MediaSessionRecord record); + protected abstract void enforcePhoneStatePermission(int pid, int uid); + abstract void updateSession(MediaSessionRecord record); + abstract void setGlobalPrioritySession(MediaSessionRecord record); + abstract List<Session2Token> getSession2TokensLocked(int userId); + abstract void onStopUser(int userId); + abstract void sessionDied(MediaSessionRecord session); + abstract void destroySession(MediaSessionRecord session); + abstract void pushSession2TokensChangedLocked(int userId); + abstract Context getContext(); + abstract IBinder getServiceBinder(); } } diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java new file mode 100644 index 000000000000..f374c6d204fd --- /dev/null +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -0,0 +1,2142 @@ +/* + * 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 com.android.server.media; + +import static android.os.UserHandle.USER_ALL; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.INotificationManager; +import android.app.KeyguardManager; +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.media.AudioManager; +import android.media.AudioManagerInternal; +import android.media.AudioPlaybackConfiguration; +import android.media.AudioSystem; +import android.media.IAudioService; +import android.media.IRemoteVolumeController; +import android.media.MediaController2; +import android.media.Session2CommandGroup; +import android.media.Session2Token; +import android.media.session.IActiveSessionsListener; +import android.media.session.ICallback; +import android.media.session.IOnMediaKeyListener; +import android.media.session.IOnVolumeKeyLongPressListener; +import android.media.session.ISession; +import android.media.session.ISession2TokensListener; +import android.media.session.ISessionManager; +import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; +import android.media.session.SessionCallbackLink; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.speech.RecognizerIntent; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * System implementation of MediaSessionManager + */ +public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { + private static final String TAG = "MediaSessionService"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + // Leave log for key event always. + private static final boolean DEBUG_KEY_EVENT = true; + + private static final int WAKELOCK_TIMEOUT = 5000; + private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; + + private final Context mContext; + private final SessionManagerImpl mSessionManagerImpl; + private final MessageHandler mHandler = new MessageHandler(); + private final PowerManager.WakeLock mMediaEventWakeLock; + private final int mLongPressTimeout; + private final INotificationManager mNotificationManager; + private final Object mLock = new Object(); + // Keeps the full user id for each user. + @GuardedBy("mLock") + private final SparseIntArray mFullUserIds = new SparseIntArray(); + @GuardedBy("mLock") + private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>(); + @GuardedBy("mLock") + private final ArrayList<SessionsListenerRecord> mSessionsListeners = + new ArrayList<SessionsListenerRecord>(); + // Map user id as index to list of Session2Tokens + // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in + // one place. + @GuardedBy("mLock") + private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>(); + @GuardedBy("mLock") + private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords = + new ArrayList<>(); + + private KeyguardManager mKeyguardManager; + private IAudioService mAudioService; + private AudioManagerInternal mAudioManagerInternal; + private ActivityManager mActivityManager; + private ContentResolver mContentResolver; + private SettingsObserver mSettingsObserver; + private boolean mHasFeatureLeanback; + + // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) + // It's always not null after the MediaSessionService is started. + private FullUserRecord mCurrentFullUserRecord; + private MediaSessionRecord mGlobalPrioritySession; + private AudioPlayerStateMonitor mAudioPlayerStateMonitor; + + // Used to notify system UI when remote volume was changed. TODO find a + // better way to handle this. + private IRemoteVolumeController mRvc; + + public MediaSessionServiceImpl(Context context) { + mContext = context; + mSessionManagerImpl = new SessionManagerImpl(); + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); + mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); + mNotificationManager = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + } + + Context getContext() { + return mContext; + } + + IBinder getServiceBinder() { + return mSessionManagerImpl; + } + + @Override + public void onStart() { + mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + mAudioService = getAudioService(); + mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); + mActivityManager = + (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); + mAudioPlayerStateMonitor.registerListener( + (config, isRemoved) -> { + if (isRemoved || !config.isActive() || config.getPlayerType() + == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { + return; + } + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked( + UserHandle.getUserId(config.getClientUid())); + if (user != null) { + user.mPriorityStack.updateMediaButtonSessionIfNeeded(); + } + } + }, null /* handler */); + mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); + mContentResolver = mContext.getContentResolver(); + mSettingsObserver = new SettingsObserver(); + mSettingsObserver.observe(); + mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LEANBACK); + + updateUser(); + } + + private IAudioService getAudioService() { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + return IAudioService.Stub.asInterface(b); + } + + private boolean isGlobalPriorityActiveLocked() { + return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); + } + + @Override + public void updateSession(MediaSessionRecord record) { + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user == null) { + Log.w(TAG, "Unknown session updated. Ignoring."); + return; + } + if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Global priority session is updated, active=" + record.isActive()); + } + user.pushAddressedPlayerChangedLocked(); + } else { + if (!user.mPriorityStack.contains(record)) { + Log.w(TAG, "Unknown session updated. Ignoring."); + return; + } + user.mPriorityStack.onSessionStateChange(record); + } + mHandler.postSessionsChanged(record.getUserId()); + } + } + + @Override + public void setGlobalPrioritySession(MediaSessionRecord record) { + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (mGlobalPrioritySession != record) { + Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession + + " to " + record); + mGlobalPrioritySession = record; + if (user != null && user.mPriorityStack.contains(record)) { + // Handle the global priority session separately. + // Otherwise, it can be the media button session regardless of the active state + // because it or other system components might have been the lastly played media + // app. + user.mPriorityStack.removeSession(record); + } + } + } + } + + private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { + List<MediaSessionRecord> records = new ArrayList<>(); + if (userId == USER_ALL) { + int size = mUserRecords.size(); + for (int i = 0; i < size; i++) { + records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); + } + } else { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "getSessions failed. Unknown user " + userId); + return records; + } + records.addAll(user.mPriorityStack.getActiveSessions(userId)); + } + + // Return global priority session at the first whenever it's asked. + if (isGlobalPriorityActiveLocked() + && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) { + records.add(0, mGlobalPrioritySession); + } + return records; + } + + List<Session2Token> getSession2TokensLocked(int userId) { + List<Session2Token> list = new ArrayList<>(); + if (userId == USER_ALL) { + for (int i = 0; i < mSession2TokensPerUser.size(); i++) { + list.addAll(mSession2TokensPerUser.valueAt(i)); + } + } else { + list.addAll(mSession2TokensPerUser.get(userId)); + } + return list; + } + + /** + * Tells the system UI that volume has changed on an active remote session. + */ + public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { + if (mRvc == null || !session.isActive()) { + return; + } + try { + mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); + } catch (Exception e) { + Log.wtf(TAG, "Error sending volume change to system UI.", e); + } + } + + @Override + public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user == null || !user.mPriorityStack.contains(record)) { + Log.d(TAG, "Unknown session changed playback state. Ignoring."); + return; + } + user.mPriorityStack.onPlaystateChanged(record, oldState, newState); + } + } + + @Override + public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user == null || !user.mPriorityStack.contains(record)) { + Log.d(TAG, "Unknown session changed playback type. Ignoring."); + return; + } + pushRemoteVolumeUpdateLocked(record.getUserId()); + } + } + + @Override + public void onStartUser(int userId) { + if (DEBUG) Log.d(TAG, "onStartUser: " + userId); + updateUser(); + } + + @Override + public void onSwitchUser(int userId) { + if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId); + updateUser(); + } + + // Called when the user with the userId is removed. + @Override + public void onStopUser(int userId) { + if (DEBUG) Log.d(TAG, "onStopUser: " + userId); + synchronized (mLock) { + // TODO: Also handle removing user in updateUser() because adding/switching user is + // handled in updateUser(). + FullUserRecord user = getFullUserRecordLocked(userId); + if (user != null) { + if (user.mFullUserId == userId) { + user.destroySessionsForUserLocked(USER_ALL); + mUserRecords.remove(userId); + } else { + user.destroySessionsForUserLocked(userId); + } + } + mSession2TokensPerUser.remove(userId); + updateUser(); + } + } + + @Override + public void monitor() { + synchronized (mLock) { + // Check for deadlock + } + } + + protected void enforcePhoneStatePermission(int pid, int uid) { + if (mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); + } + } + + void sessionDied(MediaSessionRecord session) { + synchronized (mLock) { + destroySessionLocked(session); + } + } + + void destroySession(MediaSessionRecord session) { + synchronized (mLock) { + destroySessionLocked(session); + } + } + + private void updateUser() { + synchronized (mLock) { + UserManager manager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mFullUserIds.clear(); + List<UserInfo> allUsers = manager.getUsers(); + if (allUsers != null) { + for (UserInfo userInfo : allUsers) { + if (userInfo.isManagedProfile()) { + mFullUserIds.put(userInfo.id, userInfo.profileGroupId); + } else { + mFullUserIds.put(userInfo.id, userInfo.id); + if (mUserRecords.get(userInfo.id) == null) { + mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); + } + } + if (mSession2TokensPerUser.get(userInfo.id) == null) { + mSession2TokensPerUser.put(userInfo.id, new ArrayList<>()); + } + } + } + // Ensure that the current full user exists. + int currentFullUserId = ActivityManager.getCurrentUser(); + mCurrentFullUserRecord = mUserRecords.get(currentFullUserId); + if (mCurrentFullUserRecord == null) { + Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); + mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); + mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); + if (mSession2TokensPerUser.get(currentFullUserId) == null) { + mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>()); + } + } + mFullUserIds.put(currentFullUserId, currentFullUserId); + } + } + + private void updateActiveSessionListeners() { + synchronized (mLock) { + for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { + SessionsListenerRecord listener = mSessionsListeners.get(i); + try { + enforceMediaPermissions(listener.componentName, listener.pid, listener.uid, + listener.userId); + } catch (SecurityException e) { + Log.i(TAG, "ActiveSessionsListener " + listener.componentName + + " is no longer authorized. Disconnecting."); + mSessionsListeners.remove(i); + try { + listener.listener + .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); + } catch (Exception e1) { + // ignore + } + } + } + } + } + + /* + * When a session is removed several things need to happen. + * 1. We need to remove it from the relevant user. + * 2. We need to remove it from the priority stack. + * 3. We need to remove it from all sessions. + * 4. If this is the system priority session we need to clear it. + * 5. We need to unlink to death from the cb binder + * 6. We need to tell the session to do any final cleanup (onDestroy) + */ + private void destroySessionLocked(MediaSessionRecord session) { + if (DEBUG) { + Log.d(TAG, "Destroying " + session); + } + FullUserRecord user = getFullUserRecordLocked(session.getUserId()); + if (mGlobalPrioritySession == session) { + mGlobalPrioritySession = null; + if (session.isActive() && user != null) { + user.pushAddressedPlayerChangedLocked(); + } + } else { + if (user != null) { + user.mPriorityStack.removeSession(session); + } + } + + try { + session.getCallback().getBinder().unlinkToDeath(session, 0); + } catch (Exception e) { + // ignore exceptions while destroying a session. + } + session.onDestroy(); + mHandler.postSessionsChanged(session.getUserId()); + } + + private void enforcePackageName(String packageName, int uid) { + if (TextUtils.isEmpty(packageName)) { + throw new IllegalArgumentException("packageName may not be empty"); + } + String[] packages = mContext.getPackageManager().getPackagesForUid(uid); + final int packageCount = packages.length; + for (int i = 0; i < packageCount; i++) { + if (packageName.equals(packages[i])) { + return; + } + } + throw new IllegalArgumentException("packageName is not owned by the calling process"); + } + + /** + * Checks a caller's authorization to register an IRemoteControlDisplay. + * Authorization is granted if one of the following is true: + * <ul> + * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL + * permission</li> + * <li>the caller's listener is one of the enabled notification listeners + * for the caller's user</li> + * </ul> + */ + private void enforceMediaPermissions(ComponentName compName, int pid, int uid, + int resolvedUserId) { + if (isCurrentVolumeController(pid, uid)) return; + if (mContext + .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) + != PackageManager.PERMISSION_GRANTED + && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), + resolvedUserId)) { + throw new SecurityException("Missing permission to control media."); + } + } + + private boolean isCurrentVolumeController(int pid, int uid) { + return mContext.checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, + pid, uid) == PackageManager.PERMISSION_GRANTED; + } + + private void enforceSystemUiPermission(String action, int pid, int uid) { + if (!isCurrentVolumeController(pid, uid)) { + throw new SecurityException("Only system ui may " + action); + } + } + + /** + * This checks if the component is an enabled notification listener for the + * specified user. Enabled components may only operate on behalf of the user + * they're running as. + * + * @param compName The component that is enabled. + * @param userId The user id of the caller. + * @param forUserId The user id they're making the request on behalf of. + * @return True if the component is enabled, false otherwise + */ + private boolean isEnabledNotificationListener(ComponentName compName, int userId, + int forUserId) { + if (userId != forUserId) { + // You may not access another user's content as an enabled listener. + return false; + } + if (DEBUG) { + Log.d(TAG, "Checking if enabled notification listener " + compName); + } + if (compName != null) { + try { + return mNotificationManager.isNotificationListenerAccessGrantedForUser( + compName, userId); + } catch (RemoteException e) { + Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e); + } + } + return false; + } + + private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, + String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException { + synchronized (mLock) { + return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); + } + } + + /* + * When a session is created the following things need to happen. + * 1. Its callback binder needs a link to death + * 2. It needs to be added to all sessions. + * 3. It needs to be added to the priority stack. + * 4. It needs to be added to the relevant user record. + */ + private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, + String callerPackageName, SessionCallbackLink cb, String tag) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.wtf(TAG, "Request from invalid user: " + userId); + throw new RuntimeException("Session request from invalid user."); + } + + final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, + callerPackageName, cb, tag, this, mHandler.getLooper()); + try { + cb.getBinder().linkToDeath(session, 0); + } catch (RemoteException e) { + throw new RuntimeException("Media Session owner died prematurely.", e); + } + + user.mPriorityStack.addSession(session); + mHandler.postSessionsChanged(userId); + + if (DEBUG) { + Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); + } + return session; + } + + private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { + for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { + if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) { + return i; + } + } + return -1; + } + + private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) { + for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { + if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) { + return i; + } + } + return -1; + } + + + private void pushSessionsChanged(int userId) { + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); + return; + } + List<MediaSessionRecord> records = getActiveSessionsLocked(userId); + int size = records.size(); + ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); + for (int i = 0; i < size; i++) { + tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); + } + pushRemoteVolumeUpdateLocked(userId); + for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { + SessionsListenerRecord record = mSessionsListeners.get(i); + if (record.userId == USER_ALL || record.userId == userId) { + try { + record.listener.onActiveSessionsChanged(tokens); + } catch (RemoteException e) { + Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", + e); + mSessionsListeners.remove(i); + } + } + } + } + } + + private void pushRemoteVolumeUpdateLocked(int userId) { + if (mRvc != null) { + try { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId); + return; + } + MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); + mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); + } catch (RemoteException e) { + Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); + } + } + } + + void pushSession2TokensChangedLocked(int userId) { + List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL); + List<Session2Token> session2Tokens = getSession2TokensLocked(userId); + + for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { + Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); + try { + if (listenerRecord.userId == USER_ALL) { + listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); + } else if (listenerRecord.userId == userId) { + listenerRecord.listener.onSession2TokensChanged(session2Tokens); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); + mSession2TokensListenerRecords.remove(i); + } + } + } + + /** + * Called when the media button receiver for the {@code record} is changed. + * + * @param record the media session whose media button receiver is updated. + */ + public void onMediaButtonReceiverChanged(MediaSessionRecord record) { + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + MediaSessionRecord mediaButtonSession = + user.mPriorityStack.getMediaButtonSession(); + if (record == mediaButtonSession) { + user.rememberMediaButtonReceiverLocked(mediaButtonSession); + } + } + } + + private String getCallingPackageName(int uid) { + String[] packages = mContext.getPackageManager().getPackagesForUid(uid); + if (packages != null && packages.length > 0) { + return packages[0]; + } + return ""; + } + + private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) { + if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { + return; + } + try { + mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); + } catch (RemoteException e) { + Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener"); + } + } + + private FullUserRecord getFullUserRecordLocked(int userId) { + int fullUserId = mFullUserIds.get(userId, -1); + if (fullUserId < 0) { + return null; + } + return mUserRecords.get(fullUserId); + } + + /** + * Information about a full user and its corresponding managed profiles. + * + * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate + * them when he/she presses a media/volume button. So keeping media sessions for them in one + * place makes more sense and increases the readability.</p> + * <p>The contents of this object is guarded by {@link #mLock}. + */ + final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener { + public static final int COMPONENT_TYPE_INVALID = 0; + public static final int COMPONENT_TYPE_BROADCAST = 1; + public static final int COMPONENT_TYPE_ACTIVITY = 2; + public static final int COMPONENT_TYPE_SERVICE = 3; + private static final String COMPONENT_NAME_USER_ID_DELIM = ","; + + private final int mFullUserId; + private final MediaSessionStack mPriorityStack; + private PendingIntent mLastMediaButtonReceiver; + private ComponentName mRestoredMediaButtonReceiver; + private int mRestoredMediaButtonReceiverComponentType; + private int mRestoredMediaButtonReceiverUserId; + + private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener; + private int mOnVolumeKeyLongPressListenerUid; + private KeyEvent mInitialDownVolumeKeyEvent; + private int mInitialDownVolumeStream; + private boolean mInitialDownMusicOnly; + + private IOnMediaKeyListener mOnMediaKeyListener; + private int mOnMediaKeyListenerUid; + private ICallback mCallback; + + FullUserRecord(int fullUserId) { + mFullUserId = fullUserId; + mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); + // Restore the remembered media button receiver before the boot. + String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); + if (mediaButtonReceiverInfo == null) { + return; + } + String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM); + if (tokens == null || (tokens.length != 2 && tokens.length != 3)) { + return; + } + mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]); + mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]); + if (tokens.length == 3) { + mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]); + } else { + mRestoredMediaButtonReceiverComponentType = + getComponentType(mRestoredMediaButtonReceiver); + } + } + + public void destroySessionsForUserLocked(int userId) { + List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId); + for (MediaSessionRecord session : sessions) { + MediaSessionServiceImpl.this.destroySessionLocked(session); + } + } + + public void dumpLocked(PrintWriter pw, String prefix) { + pw.print(prefix + "Record for full_user=" + mFullUserId); + // Dump managed profile user ids associated with this user. + int size = mFullUserIds.size(); + for (int i = 0; i < size; i++) { + if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i) + && mFullUserIds.valueAt(i) == mFullUserId) { + pw.print(", profile_user=" + mFullUserIds.keyAt(i)); + } + } + pw.println(); + String indent = prefix + " "; + pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener); + pw.println(indent + "Volume key long-press listener package: " + + getCallingPackageName(mOnVolumeKeyLongPressListenerUid)); + pw.println(indent + "Media key listener: " + mOnMediaKeyListener); + pw.println(indent + "Media key listener package: " + + getCallingPackageName(mOnMediaKeyListenerUid)); + pw.println(indent + "Callback: " + mCallback); + pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver); + pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver); + pw.println(indent + "Restored MediaButtonReceiverComponentType: " + + mRestoredMediaButtonReceiverComponentType); + mPriorityStack.dump(pw, indent); + pw.println(indent + "Session2Tokens:"); + for (int i = 0; i < mSession2TokensPerUser.size(); i++) { + List<Session2Token> list = mSession2TokensPerUser.valueAt(i); + if (list == null || list.size() == 0) { + continue; + } + for (Session2Token token : list) { + pw.println(indent + " " + token); + } + } + } + + @Override + public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, + MediaSessionRecord newMediaButtonSession) { + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); + } + synchronized (mLock) { + if (oldMediaButtonSession != null) { + mHandler.postSessionsChanged(oldMediaButtonSession.getUserId()); + } + if (newMediaButtonSession != null) { + rememberMediaButtonReceiverLocked(newMediaButtonSession); + mHandler.postSessionsChanged(newMediaButtonSession.getUserId()); + } + pushAddressedPlayerChangedLocked(); + } + } + + // Remember media button receiver and keep it in the persistent storage. + public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { + PendingIntent receiver = record.getMediaButtonReceiver(); + mLastMediaButtonReceiver = receiver; + mRestoredMediaButtonReceiver = null; + mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID; + + String mediaButtonReceiverInfo = ""; + if (receiver != null) { + ComponentName component = receiver.getIntent().getComponent(); + if (component != null + && record.getPackageName().equals(component.getPackageName())) { + String componentName = component.flattenToString(); + int componentType = getComponentType(component); + mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM, + componentName, String.valueOf(record.getUserId()), + String.valueOf(componentType)); + } + } + Settings.Secure.putStringForUser(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo, + mFullUserId); + } + + private void pushAddressedPlayerChangedLocked() { + if (mCallback == null) { + return; + } + try { + MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); + if (mediaButtonSession != null) { + mCallback.onAddressedPlayerChangedToMediaSession( + new MediaSession.Token(mediaButtonSession.getControllerBinder())); + } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { + mCallback.onAddressedPlayerChangedToMediaButtonReceiver( + mCurrentFullUserRecord.mLastMediaButtonReceiver + .getIntent().getComponent()); + } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { + mCallback.onAddressedPlayerChangedToMediaButtonReceiver( + mCurrentFullUserRecord.mRestoredMediaButtonReceiver); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); + } + } + + private MediaSessionRecord getMediaButtonSessionLocked() { + return isGlobalPriorityActiveLocked() + ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); + } + + private int getComponentType(@Nullable ComponentName componentName) { + if (componentName == null) { + return COMPONENT_TYPE_INVALID; + } + PackageManager pm = mContext.getPackageManager(); + try { + ActivityInfo activityInfo = pm.getActivityInfo(componentName, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.GET_ACTIVITIES); + if (activityInfo != null) { + return COMPONENT_TYPE_ACTIVITY; + } + } catch (NameNotFoundException e) { + } + try { + ServiceInfo serviceInfo = pm.getServiceInfo(componentName, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.GET_SERVICES); + if (serviceInfo != null) { + return COMPONENT_TYPE_SERVICE; + } + } catch (NameNotFoundException e) { + } + // Pick legacy behavior for BroadcastReceiver or unknown. + return COMPONENT_TYPE_BROADCAST; + } + } + + final class SessionsListenerRecord implements IBinder.DeathRecipient { + public final IActiveSessionsListener listener; + public final ComponentName componentName; + public final int userId; + public final int pid; + public final int uid; + + SessionsListenerRecord(IActiveSessionsListener listener, + ComponentName componentName, + int userId, int pid, int uid) { + this.listener = listener; + this.componentName = componentName; + this.userId = userId; + this.pid = pid; + this.uid = uid; + } + + @Override + public void binderDied() { + synchronized (mLock) { + mSessionsListeners.remove(this); + } + } + } + + final class Session2TokensListenerRecord implements IBinder.DeathRecipient { + public final ISession2TokensListener listener; + public final int userId; + + Session2TokensListenerRecord(ISession2TokensListener listener, + int userId) { + this.listener = listener; + this.userId = userId; + } + + @Override + public void binderDied() { + synchronized (mLock) { + mSession2TokensListenerRecords.remove(this); + } + } + } + + final class SettingsObserver extends ContentObserver { + private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + + private SettingsObserver() { + super(null); + } + + private void observe() { + mContentResolver.registerContentObserver(mSecureSettingsUri, + false, this, USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + updateActiveSessionListeners(); + } + } + + class SessionManagerImpl extends ISessionManager.Stub { + private static final String EXTRA_WAKELOCK_ACQUIRED = + "android.media.AudioService.WAKELOCK_ACQUIRED"; + private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number + + private boolean mVoiceButtonDown = false; + private boolean mVoiceButtonHandled = false; + + @Override + public ISession createSession(String packageName, SessionCallbackLink cb, String tag, + int userId) throws RemoteException { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + enforcePackageName(packageName, uid); + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + false /* allowAll */, true /* requireFull */, "createSession", packageName); + if (cb == null) { + throw new IllegalArgumentException("Controller callback cannot be null"); + } + return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) + .getSessionBinder(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void notifySession2Created(Session2Token sessionToken) throws RemoteException { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + if (DEBUG) { + Log.d(TAG, "Session2 is created " + sessionToken); + } + if (uid != sessionToken.getUid()) { + throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid + + " but actually=" + sessionToken.getUid()); + } + Controller2Callback callback = new Controller2Callback(sessionToken); + // Note: It's safe not to keep controller here because it wouldn't be GC'ed until + // it's closed. + // TODO: Keep controller as well for better readability + // because the GC behavior isn't straightforward. + MediaController2 controller = new MediaController2(mContext, sessionToken, + new HandlerExecutor(mHandler), callback); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public List<IBinder> getSessions(ComponentName componentName, int userId) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); + ArrayList<IBinder> binders = new ArrayList<IBinder>(); + synchronized (mLock) { + List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId); + for (MediaSessionRecord record : records) { + binders.add(record.getControllerBinder().asBinder()); + } + } + return binders; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public List<Session2Token> getSession2Tokens(int userId) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + // Check that they can make calls on behalf of the user and + // get the final user id + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + true /* allowAll */, true /* requireFull */, "getSession2Tokens", + null /* optional packageName */); + List<Session2Token> result; + synchronized (mLock) { + result = getSession2TokensLocked(resolvedUserId); + } + return result; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void addSessionsListener(IActiveSessionsListener listener, + ComponentName componentName, int userId) throws RemoteException { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); + synchronized (mLock) { + int index = findIndexOfSessionsListenerLocked(listener); + if (index != -1) { + Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); + return; + } + SessionsListenerRecord record = new SessionsListenerRecord(listener, + componentName, resolvedUserId, pid, uid); + try { + listener.asBinder().linkToDeath(record, 0); + } catch (RemoteException e) { + Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); + return; + } + mSessionsListeners.add(record); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeSessionsListener(IActiveSessionsListener listener) + throws RemoteException { + synchronized (mLock) { + int index = findIndexOfSessionsListenerLocked(listener); + if (index != -1) { + SessionsListenerRecord record = mSessionsListeners.remove(index); + try { + record.listener.asBinder().unlinkToDeath(record, 0); + } catch (Exception e) { + // ignore exceptions, the record is being removed + } + } + } + } + + @Override + public void addSession2TokensListener(ISession2TokensListener listener, + int userId) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + // Check that they can make calls on behalf of the user and get the final user id. + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + true /* allowAll */, true /* requireFull */, "addSession2TokensListener", + null /* optional packageName */); + synchronized (mLock) { + int index = findIndexOfSession2TokensListenerLocked(listener); + if (index >= 0) { + Log.w(TAG, "addSession2TokensListener is already added, ignoring"); + return; + } + mSession2TokensListenerRecords.add( + new Session2TokensListenerRecord(listener, resolvedUserId)); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeSession2TokensListener(ISession2TokensListener listener) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + synchronized (mLock) { + int index = findIndexOfSession2TokensListenerLocked(listener); + if (index >= 0) { + Session2TokensListenerRecord listenerRecord = + mSession2TokensListenerRecords.remove(index); + try { + listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0); + } catch (Exception e) { + // Ignore exception. + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Handles the dispatching of the media button events to one of the + * registered listeners, or if there was none, broadcast an + * ACTION_MEDIA_BUTTON intent to the rest of the system. + * + * @param packageName The caller package + * @param asSystemService {@code true} if the event sent to the session as if it was come + * from the system service instead of the app process. This helps sessions to + * distinguish between the key injection by the app and key events from the + * hardware devices. Should be used only when the volume key events aren't handled + * by foreground activity. {@code false} otherwise to tell session about the real + * caller. + * @param keyEvent a non-null KeyEvent whose key code is one of the + * supported media buttons + * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held + * while this key event is dispatched. + */ + @Override + public void dispatchMediaKeyEvent(String packageName, boolean asSystemService, + KeyEvent keyEvent, boolean needWakeLock) { + if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { + Log.w(TAG, "Attempted to dispatch null or non-media key event."); + return; + } + + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + if (DEBUG) { + Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid + + ", uid=" + uid + ", asSystem=" + asSystemService + ", event=" + + keyEvent); + } + if (!isUserSetupComplete()) { + // Global media key handling can have the side-effect of starting new + // activities which is undesirable while setup is in progress. + Slog.i(TAG, "Not dispatching media key event because user " + + "setup is in progress."); + return; + } + + synchronized (mLock) { + boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); + if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { + // Prevent dispatching key event through reflection while the global + // priority session is active. + Slog.i(TAG, "Only the system can dispatch media key event " + + "to the global priority session."); + return; + } + if (!isGlobalPriorityActive) { + if (mCurrentFullUserRecord.mOnMediaKeyListener != null) { + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Send " + keyEvent + " to the media key listener"); + } + try { + mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, + new MediaKeyListenerResultReceiver(packageName, pid, uid, + asSystemService, keyEvent, needWakeLock)); + return; + } catch (RemoteException e) { + Log.w(TAG, "Failed to send " + keyEvent + + " to the media key listener"); + } + } + } + if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) { + handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, + needWakeLock); + } else { + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, needWakeLock); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setCallback(ICallback callback) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) { + throw new SecurityException("Only Bluetooth service processes can set" + + " Callback"); + } + synchronized (mLock) { + int userId = UserHandle.getUserId(uid); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can set the callback" + + ", userId=" + userId); + return; + } + user.mCallback = callback; + Log.d(TAG, "The callback " + user.mCallback + + " is set by " + getCallingPackageName(uid)); + if (user.mCallback == null) { + return; + } + try { + user.mCallback.asBinder().linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mLock) { + user.mCallback = null; + } + } + }, 0); + user.pushAddressedPlayerChangedLocked(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to set callback", e); + user.mCallback = null; + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission. + if (mContext.checkPermission( + android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" + + " permission."); + } + + synchronized (mLock) { + int userId = UserHandle.getUserId(uid); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can set the volume key long-press listener" + + ", userId=" + userId); + return; + } + if (user.mOnVolumeKeyLongPressListener != null + && user.mOnVolumeKeyLongPressListenerUid != uid) { + Log.w(TAG, "The volume key long-press listener cannot be reset" + + " by another app , mOnVolumeKeyLongPressListener=" + + user.mOnVolumeKeyLongPressListenerUid + + ", uid=" + uid); + return; + } + + user.mOnVolumeKeyLongPressListener = listener; + user.mOnVolumeKeyLongPressListenerUid = uid; + + Log.d(TAG, "The volume key long-press listener " + + listener + " is set by " + getCallingPackageName(uid)); + + if (user.mOnVolumeKeyLongPressListener != null) { + try { + user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mLock) { + user.mOnVolumeKeyLongPressListener = null; + } + } + }, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to set death recipient " + + user.mOnVolumeKeyLongPressListener); + user.mOnVolumeKeyLongPressListener = null; + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setOnMediaKeyListener(IOnMediaKeyListener listener) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + // Enforce SET_MEDIA_KEY_LISTENER permission. + if (mContext.checkPermission( + android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER permission."); + } + + synchronized (mLock) { + int userId = UserHandle.getUserId(uid); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can set the media key listener" + + ", userId=" + userId); + return; + } + if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) { + Log.w(TAG, "The media key listener cannot be reset by another app. " + + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid + + ", uid=" + uid); + return; + } + + user.mOnMediaKeyListener = listener; + user.mOnMediaKeyListenerUid = uid; + + Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener + + " is set by " + getCallingPackageName(uid)); + + if (user.mOnMediaKeyListener != null) { + try { + user.mOnMediaKeyListener.asBinder().linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mLock) { + user.mOnMediaKeyListener = null; + } + } + }, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener); + user.mOnMediaKeyListener = null; + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Handles the dispatching of the volume button events to one of the + * registered listeners. If there's a volume key long-press listener and + * there's no active global priority session, long-pressess will be sent to the + * long-press listener instead of adjusting volume. + * + * @param packageName The caller's package name, obtained by Context#getPackageName() + * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName() + * @param asSystemService {@code true} if the event sent to the session as if it was come + * from the system service instead of the app process. This helps sessions to + * distinguish between the key injection by the app and key events from the + * hardware devices. Should be used only when the volume key events aren't handled + * by foreground activity. {@code false} otherwise to tell session about the real + * caller. + * @param keyEvent a non-null KeyEvent whose key code is one of the + * {@link KeyEvent#KEYCODE_VOLUME_UP}, + * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, + * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. + * @param stream stream type to adjust volume. + * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume. + */ + @Override + public void dispatchVolumeKeyEvent(String packageName, String opPackageName, + boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) { + if (keyEvent == null + || (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP + && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN + && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) { + Log.w(TAG, "Attempted to dispatch null or non-volume key event."); + return; + } + + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid=" + + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent); + } + + try { + synchronized (mLock) { + if (isGlobalPriorityActiveLocked() + || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { + dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, + asSystemService, keyEvent, stream, musicOnly); + } else { + // TODO: Consider the case when both volume up and down keys are pressed + // at the same time. + if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + if (keyEvent.getRepeatCount() == 0) { + // Keeps the copy of the KeyEvent because it can be reused. + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = + KeyEvent.obtain(keyEvent); + mCurrentFullUserRecord.mInitialDownVolumeStream = stream; + mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly; + mHandler.sendMessageDelayed( + mHandler.obtainMessage( + MessageHandler.MSG_VOLUME_INITIAL_DOWN, + mCurrentFullUserRecord.mFullUserId, 0), + mLongPressTimeout); + } + if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { + mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); + if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) { + dispatchVolumeKeyLongPressLocked( + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent); + // Mark that the key is already handled. + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null; + } + dispatchVolumeKeyLongPressLocked(keyEvent); + } + } else { // if up + mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); + if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null + && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent + .getDownTime() == keyEvent.getDownTime()) { + // Short-press. Should change volume. + dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, + asSystemService, + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent, + mCurrentFullUserRecord.mInitialDownVolumeStream, + mCurrentFullUserRecord.mInitialDownMusicOnly); + dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, + asSystemService, keyEvent, stream, musicOnly); + } else { + dispatchVolumeKeyLongPressLocked(keyEvent); + } + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid, + int uid, boolean asSystemService, KeyEvent keyEvent, int stream, + boolean musicOnly) { + boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; + boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; + int direction = 0; + boolean isMute = false; + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_VOLUME_UP: + direction = AudioManager.ADJUST_RAISE; + break; + case KeyEvent.KEYCODE_VOLUME_DOWN: + direction = AudioManager.ADJUST_LOWER; + break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + isMute = true; + break; + } + if (down || up) { + int flags = AudioManager.FLAG_FROM_KEY; + if (musicOnly) { + // This flag is used when the screen is off to only affect active media. + flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY; + } else { + // These flags are consistent with the home screen + if (up) { + flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; + } else { + flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; + } + } + if (direction != 0) { + // If this is action up we want to send a beep for non-music events + if (up) { + direction = 0; + } + dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, + asSystemService, stream, direction, flags); + } else if (isMute) { + if (down && keyEvent.getRepeatCount() == 0) { + dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, + asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags); + } + } + } + } + + @Override + public void dispatchAdjustVolume(String packageName, String opPackageName, + int suggestedStream, int delta, int flags) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false, + suggestedStream, delta, flags); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setRemoteVolumeController(IRemoteVolumeController rvc) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + enforceSystemUiPermission("listen for volume changes", pid, uid); + mRvc = rvc; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public boolean isGlobalPriorityActive() { + synchronized (mLock) { + return isGlobalPriorityActiveLocked(); + } + } + + @Override + public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + + pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); + pw.println(); + + synchronized (mLock) { + pw.println(mSessionsListeners.size() + " sessions listeners."); + pw.println("Global priority session is " + mGlobalPrioritySession); + if (mGlobalPrioritySession != null) { + mGlobalPrioritySession.dump(pw, " "); + } + pw.println("User Records:"); + int count = mUserRecords.size(); + for (int i = 0; i < count; i++) { + mUserRecords.valueAt(i).dumpLocked(pw, ""); + } + mAudioPlayerStateMonitor.dump(mContext, pw, ""); + } + } + + /** + * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL + * permission or an enabled notification listener) + * + * @param controllerPackageName package name of the controller app + * @param controllerPid pid of the controller app + * @param controllerUid uid of the controller app + */ + @Override + public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid) + throws RemoteException { + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + // Don't perform sanity check between controllerPackageName and controllerUid. + // When an (activity|service) runs on the another apps process by specifying + // android:process in the AndroidManifest.xml, then PID and UID would have the + // running process' information instead of the (activity|service) that has created + // MediaController. + // Note that we can use Context#getOpPackageName() instead of + // Context#getPackageName() for getting package name that matches with the PID/UID, + // but it doesn't tell which package has created the MediaController, so useless. + return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName, + controllerPid, controllerUid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + // For MediaSession + private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, + final int uid) { + String packageName = null; + if (componentName != null) { + // If they gave us a component name verify they own the + // package + packageName = componentName.getPackageName(); + enforcePackageName(packageName, uid); + } + // Check that they can make calls on behalf of the user and + // get the final user id + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + true /* allowAll */, true /* requireFull */, "getSessions", packageName); + // Check if they have the permissions or their component is + // enabled for the user they're calling from. + enforceMediaPermissions(componentName, pid, uid, resolvedUserId); + return resolvedUserId; + } + + private boolean hasMediaControlPermission(int resolvedUserId, String packageName, + int pid, int uid) throws RemoteException { + // Allow API calls from the System UI + if (isCurrentVolumeController(pid, uid)) { + return true; + } + + // Check if it's system server or has MEDIA_CONTENT_CONTROL. + // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra + // check here. + if (uid == Process.SYSTEM_UID || mContext.checkPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) + == PackageManager.PERMISSION_GRANTED) { + return true; + } else if (DEBUG) { + Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); + } + + // You may not access another user's content as an enabled listener. + final int userId = UserHandle.getUserId(uid); + if (resolvedUserId != userId) { + return false; + } + + // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( + // String pkgName) to notification team for optimization + final List<ComponentName> enabledNotificationListeners = + mNotificationManager.getEnabledNotificationListeners(userId); + if (enabledNotificationListeners != null) { + for (int i = 0; i < enabledNotificationListeners.size(); i++) { + if (TextUtils.equals(packageName, + enabledNotificationListeners.get(i).getPackageName())) { + return true; + } + } + } + if (DEBUG) { + Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " + + "notification listener"); + } + return false; + } + + private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid, + int uid, boolean asSystemService, int suggestedStream, int direction, int flags) { + MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession + : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); + + boolean preferSuggestedStream = false; + if (isValidLocalStreamType(suggestedStream) + && AudioSystem.isStreamActive(suggestedStream, 0)) { + preferSuggestedStream = true; + } + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags=" + + flags + ", suggestedStream=" + suggestedStream + + ", preferSuggestedStream=" + preferSuggestedStream); + } + if (session == null || preferSuggestedStream) { + if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0 + && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { + if (DEBUG) { + Log.d(TAG, "No active session to adjust, skipping media only volume event"); + } + return; + } + + // Execute mAudioService.adjustSuggestedStreamVolume() on + // handler thread of MediaSessionService. + // This will release the MediaSessionService.mLock sooner and avoid + // a potential deadlock between MediaSessionService.mLock and + // ActivityManagerService lock. + mHandler.post(new Runnable() { + @Override + public void run() { + final String callingOpPackageName; + final int callingUid; + if (asSystemService) { + callingOpPackageName = mContext.getOpPackageName(); + callingUid = Process.myUid(); + } else { + callingOpPackageName = opPackageName; + callingUid = uid; + } + try { + mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream, + direction, flags, callingOpPackageName, callingUid); + } catch (SecurityException | IllegalArgumentException e) { + Log.e(TAG, "Cannot adjust volume: direction=" + direction + + ", suggestedStream=" + suggestedStream + ", flags=" + flags + + ", packageName=" + packageName + ", uid=" + uid + + ", asSystemService=" + asSystemService, e); + } + } + }); + } else { + session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService, + direction, flags, true); + } + } + + private void handleVoiceKeyEventLocked(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { + int action = keyEvent.getAction(); + boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; + if (action == KeyEvent.ACTION_DOWN) { + if (keyEvent.getRepeatCount() == 0) { + mVoiceButtonDown = true; + mVoiceButtonHandled = false; + } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) { + mVoiceButtonHandled = true; + startVoiceInput(needWakeLock); + } + } else if (action == KeyEvent.ACTION_UP) { + if (mVoiceButtonDown) { + mVoiceButtonDown = false; + if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { + // Resend the down then send this event through + KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + downEvent, needWakeLock); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, needWakeLock); + } + } + } + } + + private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { + MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); + if (session != null) { + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Sending " + keyEvent + " to " + session); + } + if (needWakeLock) { + mKeyEventReceiver.aquireWakeLockLocked(); + } + // If we don't need a wakelock use -1 as the id so we won't release it later. + session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent, + needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, + mKeyEventReceiver); + if (mCurrentFullUserRecord.mCallback != null) { + try { + mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession( + keyEvent, new MediaSession.Token(session.getControllerBinder())); + } catch (RemoteException e) { + Log.w(TAG, "Failed to send callback", e); + } + } + } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null + || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { + if (needWakeLock) { + mKeyEventReceiver.aquireWakeLockLocked(); + } + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + // TODO: Find a way to also send PID/UID in secure way. + String callerPackageName = + (asSystemService) ? mContext.getPackageName() : packageName; + mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName); + try { + if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { + PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver; + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Sending " + keyEvent + + " to the last known PendingIntent " + receiver); + } + receiver.send(mContext, + needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, + mediaButtonIntent, mKeyEventReceiver, mHandler); + if (mCurrentFullUserRecord.mCallback != null) { + ComponentName componentName = mCurrentFullUserRecord + .mLastMediaButtonReceiver.getIntent().getComponent(); + if (componentName != null) { + mCurrentFullUserRecord.mCallback + .onMediaKeyEventDispatchedToMediaButtonReceiver( + keyEvent, componentName); + } + } + } else { + ComponentName receiver = + mCurrentFullUserRecord.mRestoredMediaButtonReceiver; + int componentType = mCurrentFullUserRecord + .mRestoredMediaButtonReceiverComponentType; + UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord + .mRestoredMediaButtonReceiverUserId); + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Sending " + keyEvent + " to the restored intent " + + receiver + ", type=" + componentType); + } + mediaButtonIntent.setComponent(receiver); + try { + switch (componentType) { + case FullUserRecord.COMPONENT_TYPE_ACTIVITY: + mContext.startActivityAsUser(mediaButtonIntent, userHandle); + break; + case FullUserRecord.COMPONENT_TYPE_SERVICE: + mContext.startForegroundServiceAsUser(mediaButtonIntent, + userHandle); + break; + default: + // Legacy behavior for other cases. + mContext.sendBroadcastAsUser(mediaButtonIntent, userHandle); + } + } catch (Exception e) { + Log.w(TAG, "Error sending media button to the restored intent " + + receiver + ", type=" + componentType, e); + } + if (mCurrentFullUserRecord.mCallback != null) { + mCurrentFullUserRecord.mCallback + .onMediaKeyEventDispatchedToMediaButtonReceiver( + keyEvent, receiver); + } + } + } catch (CanceledException e) { + Log.i(TAG, "Error sending key event to media button receiver " + + mCurrentFullUserRecord.mLastMediaButtonReceiver, e); + } catch (RemoteException e) { + Log.w(TAG, "Failed to send callback", e); + } + } + } + + private void startVoiceInput(boolean needWakeLock) { + Intent voiceIntent = null; + // select which type of search to launch: + // - screen on and device unlocked: action is ACTION_WEB_SEARCH + // - device locked or screen off: action is + // ACTION_VOICE_SEARCH_HANDS_FREE + // with EXTRA_SECURE set to true if the device is securely locked + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); + if (!isLocked && pm.isScreenOn()) { + voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); + Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); + } else { + voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); + voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, + isLocked && mKeyguardManager.isKeyguardSecure()); + Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); + } + // start the search activity + if (needWakeLock) { + mMediaEventWakeLock.acquire(); + } + try { + if (voiceIntent != null) { + voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent); + mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT); + } + } catch (ActivityNotFoundException e) { + Log.w(TAG, "No activity for search: " + e); + } finally { + if (needWakeLock) { + mMediaEventWakeLock.release(); + } + } + } + + private boolean isVoiceKey(int keyCode) { + return keyCode == KeyEvent.KEYCODE_HEADSETHOOK + || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); + } + + private boolean isUserSetupComplete() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; + } + + // we only handle public stream types, which are 0-5 + private boolean isValidLocalStreamType(int streamType) { + return streamType >= AudioManager.STREAM_VOICE_CALL + && streamType <= AudioManager.STREAM_NOTIFICATION; + } + + private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { + private final String mPackageName; + private final int mPid; + private final int mUid; + private final boolean mAsSystemService; + private final KeyEvent mKeyEvent; + private final boolean mNeedWakeLock; + private boolean mHandled; + + private MediaKeyListenerResultReceiver(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { + super(mHandler); + mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT); + mPackageName = packageName; + mPid = pid; + mUid = uid; + mAsSystemService = asSystemService; + mKeyEvent = keyEvent; + mNeedWakeLock = needWakeLock; + } + + @Override + public void run() { + Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent); + dispatchMediaKeyEvent(); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) { + mHandled = true; + mHandler.removeCallbacks(this); + return; + } + dispatchMediaKeyEvent(); + } + + private void dispatchMediaKeyEvent() { + if (mHandled) { + return; + } + mHandled = true; + mHandler.removeCallbacks(this); + synchronized (mLock) { + if (!isGlobalPriorityActiveLocked() + && isVoiceKey(mKeyEvent.getKeyCode())) { + handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, + mKeyEvent, mNeedWakeLock); + } else { + dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, + mKeyEvent, mNeedWakeLock); + } + } + } + } + + private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); + + class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, + PendingIntent.OnFinished { + private final Handler mHandler; + private int mRefCount = 0; + private int mLastTimeoutId = 0; + + KeyEventWakeLockReceiver(Handler handler) { + super(handler); + mHandler = handler; + } + + public void onTimeout() { + synchronized (mLock) { + if (mRefCount == 0) { + // We've already released it, so just return + return; + } + mLastTimeoutId++; + mRefCount = 0; + releaseWakeLockLocked(); + } + } + + public void aquireWakeLockLocked() { + if (mRefCount == 0) { + mMediaEventWakeLock.acquire(); + } + mRefCount++; + mHandler.removeCallbacks(this); + mHandler.postDelayed(this, WAKELOCK_TIMEOUT); + + } + + @Override + public void run() { + onTimeout(); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode < mLastTimeoutId) { + // Ignore results from calls that were before the last + // timeout, just in case. + return; + } else { + synchronized (mLock) { + if (mRefCount > 0) { + mRefCount--; + if (mRefCount == 0) { + releaseWakeLockLocked(); + } + } + } + } + } + + private void releaseWakeLockLocked() { + mMediaEventWakeLock.release(); + mHandler.removeCallbacks(this); + } + + @Override + public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, + String resultData, Bundle resultExtras) { + onReceiveResult(resultCode, null); + } + }; + + BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) { + return; + } + Bundle extras = intent.getExtras(); + if (extras == null) { + return; + } + synchronized (mLock) { + if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) + && mMediaEventWakeLock.isHeld()) { + mMediaEventWakeLock.release(); + } + } + } + }; + } + + final class MessageHandler extends Handler { + private static final int MSG_SESSIONS_CHANGED = 1; + private static final int MSG_VOLUME_INITIAL_DOWN = 2; + private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SESSIONS_CHANGED: + pushSessionsChanged((int) msg.obj); + break; + case MSG_VOLUME_INITIAL_DOWN: + synchronized (mLock) { + FullUserRecord user = mUserRecords.get((int) msg.arg1); + if (user != null && user.mInitialDownVolumeKeyEvent != null) { + dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent); + // Mark that the key is already handled. + user.mInitialDownVolumeKeyEvent = null; + } + } + break; + } + } + + public void postSessionsChanged(int userId) { + // Use object instead of the arguments when posting message to remove pending requests. + Integer userIdInteger = mIntegerCache.get(userId); + if (userIdInteger == null) { + userIdInteger = Integer.valueOf(userId); + mIntegerCache.put(userId, userIdInteger); + } + removeMessages(MSG_SESSIONS_CHANGED, userIdInteger); + obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget(); + } + } + + private class Controller2Callback extends MediaController2.ControllerCallback { + private final Session2Token mToken; + + Controller2Callback(Session2Token token) { + mToken = token; + } + + @Override + public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { + synchronized (mLock) { + int userId = UserHandle.getUserId(mToken.getUid()); + mSession2TokensPerUser.get(userId).add(mToken); + pushSession2TokensChangedLocked(userId); + } + } + + @Override + public void onDisconnected(MediaController2 controller) { + synchronized (mLock) { + int userId = UserHandle.getUserId(mToken.getUid()); + mSession2TokensPerUser.get(userId).remove(mToken); + pushSession2TokensChangedLocked(userId); + } + } + } +} diff --git a/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java b/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java index 6a5f563683a3..64c451d03caa 100644 --- a/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java +++ b/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java @@ -159,12 +159,13 @@ public final class RemoteDisplayProviderWatcher { + serviceInfo.packageName + "/" + serviceInfo.name); return false; } - if (!hasCaptureVideoPermission(serviceInfo.packageName)) { - // If the service does not have permission to capture video then it - // isn't going to be terribly useful as a remote display, is it? - // Kind of makes you wonder what it's doing there in the first place. + if (mPackageManager.checkPermission(Manifest.permission.REMOTE_DISPLAY_PROVIDER, + serviceInfo.packageName) != PackageManager.PERMISSION_GRANTED) { + // If the service does not have this permission then the system will not bind to it. + // This is to prevent non privileged apps declaring themselves as remote display + // providers just to be bound to by the system and keep their process alive. Slog.w(TAG, "Ignoring remote display provider service because it does not " - + "have the CAPTURE_VIDEO_OUTPUT or CAPTURE_SECURE_VIDEO_OUTPUT " + + "have the REMOTE_DISPLAY_PROVIDER " + "permission: " + serviceInfo.packageName + "/" + serviceInfo.name); return false; } @@ -172,18 +173,6 @@ public final class RemoteDisplayProviderWatcher { return true; } - private boolean hasCaptureVideoPermission(String packageName) { - if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT, - packageName) == PackageManager.PERMISSION_GRANTED) { - return true; - } - if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT, - packageName) == PackageManager.PERMISSION_GRANTED) { - return true; - } - return false; - } - private int findProvider(String packageName, String className) { int count = mProviders.size(); for (int i = 0; i < count; i++) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 20eebe7bf2fc..7323e93ebf84 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2311,22 +2311,22 @@ public class NotificationManagerService extends SystemService { } @Override - public boolean areAppOverlaysAllowed(String pkg) { - return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid()); + public boolean areBubblesAllowed(String pkg) { + return areBubblesAllowedForPackage(pkg, Binder.getCallingUid()); } @Override - public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) { - enforceSystemOrSystemUIOrSamePackage("Caller not system or systemui or same package", - pkg); - return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid); + public boolean areBubblesAllowedForPackage(String pkg, int uid) { + enforceSystemOrSystemUIOrSamePackage(pkg, + "Caller not system or systemui or same package"); + return mPreferencesHelper.areBubblessAllowed(pkg, uid); } @Override - public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + public void setBubblesAllowed(String pkg, int uid, boolean allowed) { checkCallerIsSystem(); - mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed); + mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed); handleSavePolicyFile(); } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 28f6972636be..7a21aa208dfd 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -81,7 +81,7 @@ public class PreferencesHelper implements RankingConfig { private static final String ATT_NAME = "name"; private static final String ATT_UID = "uid"; private static final String ATT_ID = "id"; - private static final String ATT_APP_OVERLAY = "overlay"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; private static final String ATT_PRIORITY = "priority"; private static final String ATT_VISIBILITY = "visibility"; private static final String ATT_IMPORTANCE = "importance"; @@ -94,8 +94,9 @@ public class PreferencesHelper implements RankingConfig { private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true; + private static final boolean DEFAULT_ALLOW_BUBBLE = true; private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false; + /** * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable * fields. @@ -108,7 +109,7 @@ public class PreferencesHelper implements RankingConfig { @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE}) public @interface LockableAppFields { int USER_LOCKED_IMPORTANCE = 0x00000001; - int USER_LOCKED_APP_OVERLAY = 0x00000002; + int USER_LOCKED_BUBBLE = 0x00000002; } // pkg|uid => PackagePreferences @@ -176,7 +177,7 @@ public class PreferencesHelper implements RankingConfig { XmlUtils.readBooleanAttribute( parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), XmlUtils.readBooleanAttribute( - parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); + parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); r.importance = XmlUtils.readIntAttribute( parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); r.priority = XmlUtils.readIntAttribute( @@ -272,11 +273,11 @@ public class PreferencesHelper implements RankingConfig { private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) { return getOrCreatePackagePreferences(pkg, uid, DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE, - DEFAULT_ALLOW_APP_OVERLAY); + DEFAULT_ALLOW_BUBBLE); } private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance, - int priority, int visibility, boolean showBadge, boolean allowAppOverlay) { + int priority, int visibility, boolean showBadge, boolean allowBubble) { final String key = packagePreferencesKey(pkg, uid); synchronized (mPackagePreferences) { PackagePreferences @@ -290,7 +291,7 @@ public class PreferencesHelper implements RankingConfig { r.priority = priority; r.visibility = visibility; r.showBadge = showBadge; - r.appOverlay = allowAppOverlay; + r.allowBubble = allowBubble; try { createDefaultChannelIfNeeded(r); @@ -392,7 +393,7 @@ public class PreferencesHelper implements RankingConfig { || r.channels.size() > 0 || r.groups.size() > 0 || r.delegate != null - || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY; + || r.allowBubble != DEFAULT_ALLOW_BUBBLE; if (hasNonDefaultSettings) { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); @@ -405,8 +406,8 @@ public class PreferencesHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); } - if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) { - out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay)); + if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) { + out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble)); } out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); out.attribute(null, ATT_APP_USER_LOCKED_FIELDS, @@ -452,14 +453,28 @@ public class PreferencesHelper implements RankingConfig { out.endTag(null, TAG_RANKING); } - public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + /** + * Sets whether bubbles are allowed. + * + * @param pkg the package to allow or not allow bubbles for. + * @param uid the uid to allow or not allow bubbles for. + * @param allowed whether bubbles are allowed. + */ + public void setBubblesAllowed(String pkg, int uid, boolean allowed) { PackagePreferences p = getOrCreatePackagePreferences(pkg, uid); - p.appOverlay = allowed; - p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY; + p.allowBubble = allowed; + p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE; } - public boolean areAppOverlaysAllowed(String pkg, int uid) { - return getOrCreatePackagePreferences(pkg, uid).appOverlay; + /** + * Whether bubbles are allowed. + * + * @param pkg the package to check if bubbles are allowed for + * @param uid the uid to check if bubbles are allowed for. + * @return whether bubbles are allowed. + */ + public boolean areBubblessAllowed(String pkg, int uid) { + return getOrCreatePackagePreferences(pkg, uid).allowBubble; } public int getAppLockedFields(String pkg, int uid) { @@ -1232,8 +1247,8 @@ public class PreferencesHelper implements RankingConfig { if (original.canShowBadge() != update.canShowBadge()) { update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); } - if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) { - update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY); + if (original.isBubbleAllowed() != update.isBubbleAllowed()) { + update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE); } } @@ -1654,7 +1669,7 @@ public class PreferencesHelper implements RankingConfig { int priority = DEFAULT_PRIORITY; int visibility = DEFAULT_VISIBILITY; boolean showBadge = DEFAULT_SHOW_BADGE; - boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY; + boolean allowBubble = DEFAULT_ALLOW_BUBBLE; int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE; List<String> futureOemLockedChannels = new ArrayList<>(); diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index 16143d3ae9e0..74fbea1544bd 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -24,11 +24,14 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.content.om.OverlayInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.os.Build.VERSION_CODES; import android.os.IBinder; import android.os.IIdmap2; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.util.Slog; @@ -51,6 +54,13 @@ class IdmapManager { private final Installer mInstaller; private IIdmap2 mIdmap2Service; + private static final boolean VENDOR_IS_Q_OR_LATER; + static { + // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized + final String value = SystemProperties.get("ro.vndk.version", "Q"); + VENDOR_IS_Q_OR_LATER = value.equals("Q") || value.equals("q"); + } + IdmapManager(final Installer installer) { mInstaller = installer; if (FEATURE_FLAG_IDMAP2) { @@ -69,10 +79,13 @@ class IdmapManager { final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath(); try { if (FEATURE_FLAG_IDMAP2) { - if (mIdmap2Service.verifyIdmap(overlayPath, userId)) { + int policies = determineFulfilledPolicies(overlayPackage); + boolean enforce = enforceOverlayable(overlayPackage); + if (mIdmap2Service.verifyIdmap(overlayPath, policies, enforce, userId)) { return true; } - return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null; + return mIdmap2Service.createIdmap(targetPath, overlayPath, policies, enforce, + userId) != null; } else { mInstaller.idmap(targetPath, overlayPath, sharedGid); return true; @@ -156,4 +169,71 @@ class IdmapManager { }, SECOND_IN_MILLIS); } } + + /** + * Checks if overlayable and policies should be enforced on the specified overlay for backwards + * compatibility with pre-Q overlays. + */ + private boolean enforceOverlayable(@NonNull final PackageInfo overlayPackage) { + final ApplicationInfo ai = overlayPackage.applicationInfo; + if (ai.targetSdkVersion >= VERSION_CODES.Q) { + // Always enforce policies for overlays targeting Q+. + return true; + } + + if (ai.isVendor() && !VENDOR_IS_Q_OR_LATER) { + // If the overlay is on a pre-Q vendor partition, do not enforce overlayable + // restrictions on this overlay because the pre-Q platform has no understanding of + // overlayable. + return false; + } + + // Do not enforce overlayable restrictions on pre-Q overlays signed with the + // platform signature. + return !ai.isSignedWithPlatformKey(); + } + + /** + * Retrieves a bitmask for idmap2 that represents the policies the specified overlay fulfills. + * @throws SecurityException if the overlay is not allowed to overlay any resource + */ + private int determineFulfilledPolicies(@NonNull final PackageInfo overlayPackage) + throws SecurityException { + final ApplicationInfo ai = overlayPackage.applicationInfo; + final boolean overlayIsQOrLater = ai.targetSdkVersion >= VERSION_CODES.Q; + + int fulfilledPolicies = 0; + + // TODO(b/119402606) : Add signature policy + + // Vendor partition (/vendor) + if (ai.isVendor()) { + if (overlayIsQOrLater) { + fulfilledPolicies |= IIdmap2.POLICY_VENDOR_PARTITION; + } else if (VENDOR_IS_Q_OR_LATER) { + throw new SecurityException("Overlay must target Q sdk or higher"); + } + } + + // Product partition (/product) + if (ai.isProduct()) { + if (overlayIsQOrLater) { + fulfilledPolicies |= IIdmap2.POLICY_PRODUCT_PARTITION; + } else { + throw new SecurityException("Overlay must target Q sdk or higher"); + } + } + + // System partition (/system) + if (ai.isSystemApp()) { + if (overlayIsQOrLater) { + fulfilledPolicies |= IIdmap2.POLICY_SYSTEM_PARTITION; + } else { + throw new SecurityException("Overlay must target Q sdk or higher"); + } + } + + // All overlays can overlay resources with the public policy + return fulfilledPolicies | IIdmap2.POLICY_PUBLIC; + } } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index b0d2704196a6..1cbf0bfac05c 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -270,7 +270,9 @@ final class OverlayManagerServiceImpl { Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId); } - updateAllOverlaysForTarget(packageName, userId, 0); + if (updateAllOverlaysForTarget(packageName, userId, 0)) { + mListener.onOverlaysChanged(packageName, userId); + } } void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 945d7ad008d4..6fe32c5677d4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -239,6 +239,7 @@ import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; +import android.permission.PermissionControllerManager; import android.provider.MediaStore; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -317,7 +318,6 @@ import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionsState; -import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -370,6 +370,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -443,6 +444,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean ENABLE_FREE_CACHE_V2 = SystemProperties.getBoolean("fw.free_cache_v2", true); + private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60); + private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; @@ -1339,6 +1342,8 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mSystemTextClassifierPackage; final @Nullable String mWellbeingPackage; final @Nullable String mDocumenterPackage; + final @Nullable String mConfiguratorPackage; + final @Nullable String mAppPredictionServicePackage; final @NonNull String mServicesSystemSharedLibraryPackageName; final @NonNull String mSharedSystemSharedLibraryPackageName; @@ -2862,6 +2867,9 @@ public class PackageManagerService extends IPackageManager.Stub mWellbeingPackage = getWellbeingPackageName(); mDocumenterPackage = getDocumenterPackageName(); + mConfiguratorPackage = + mContext.getString(R.string.config_deviceConfiguratorPackageName); + mAppPredictionServicePackage = getAppPredictionServicePackageName(); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -3744,7 +3752,7 @@ public class PackageManagerService extends IPackageManager.Stub /** * Returns whether or not a full application can see an instant application. * <p> - * Currently, there are three cases in which this can occur: + * Currently, there are four cases in which this can occur: * <ol> * <li>The calling application is a "special" process. Special processes * are those with a UID < {@link Process#FIRST_APPLICATION_UID}.</li> @@ -3752,6 +3760,7 @@ public class PackageManagerService extends IPackageManager.Stub * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}.</li> * <li>The calling application is the default launcher on the * system partition.</li> + * <li>The calling application is the default app prediction service.</li> * </ol> */ private boolean canViewInstantApps(int callingUid, int userId) { @@ -3769,6 +3778,11 @@ public class PackageManagerService extends IPackageManager.Stub && isCallerSameApp(homeComponent.getPackageName(), callingUid)) { return true; } + // TODO(b/122900055) Change/Remove this and replace with new permission role. + if (mAppPredictionServicePackage != null + && isCallerSameApp(mAppPredictionServicePackage, callingUid)) { + return true; + } } return false; } @@ -5484,13 +5498,13 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; // Map to base uids. - uid1 = UserHandle.getAppId(uid1); - uid2 = UserHandle.getAppId(uid2); + final int appId1 = UserHandle.getAppId(uid1); + final int appId2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; Signature[] s2; - Object obj = mSettings.getSettingLPr(uid1); + Object obj = mSettings.getSettingLPr(appId1); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { @@ -5509,7 +5523,7 @@ public class PackageManagerService extends IPackageManager.Stub } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } - obj = mSettings.getSettingLPr(uid2); + obj = mSettings.getSettingLPr(appId2); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { @@ -5564,11 +5578,11 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); // Map to base uids. - uid = UserHandle.getAppId(uid); + final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final PackageParser.SigningDetails signingDetails; - final Object obj = mSettings.getSettingLPr(uid); + final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { if (obj instanceof SharedUserSetting) { final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; @@ -5684,10 +5698,10 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); - uid = UserHandle.getAppId(uid); + final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { - Object obj = mSettings.getSettingLPr(uid); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return null; @@ -5722,8 +5736,9 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(callingUid) != null) { return null; } + final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { - Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.name + ":" + sus.userId; @@ -5750,8 +5765,8 @@ public class PackageManagerService extends IPackageManager.Stub final String[] names = new String[uids.length]; synchronized (mPackages) { for (int i = uids.length - 1; i >= 0; i--) { - final int uid = uids[i]; - Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); + final int appId = UserHandle.getAppId(uids[i]); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; names[i] = "shared:" + sus.name; @@ -5799,8 +5814,9 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(callingUid) != null) { return 0; } + final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { - Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.pkgFlags; @@ -5821,8 +5837,9 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(callingUid) != null) { return 0; } + final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { - Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.pkgPrivateFlags; @@ -5842,10 +5859,10 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } - uid = UserHandle.getAppId(uid); + final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { - Object obj = mSettings.getSettingLPr(uid); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final Iterator<PackageSetting> it = sus.packages.iterator(); @@ -13775,6 +13792,11 @@ public class PackageManagerService extends IPackageManager.Stub ServiceManager.getService(Context.BACKUP_SERVICE)); if (bm != null) { int userId = args.user.getIdentifier(); + // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM + // in the BackupManager. USER_ALL is used in compatibility tests. + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_SYSTEM; + } if (DEBUG_INSTALL) { Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId); } @@ -18941,7 +18963,8 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private int getUidTargetSdkVersionLockedLPr(int uid) { - Object obj = mSettings.getSettingLPr(uid); + final int appId = UserHandle.getAppId(uid); + final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; int vers = Build.VERSION_CODES.CUR_DEVELOPMENT; @@ -19132,7 +19155,7 @@ public class PackageManagerService extends IPackageManager.Stub // writer synchronized (mPackages) { PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null || pkg.applicationInfo.uid != callingUid) { + if (pkg == null || !isCallerSameApp(packageName, callingUid)) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { @@ -19573,28 +19596,32 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Only the system may call getPermissionGrantBackup()"); } - ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - try { - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); - serializer.startDocument(null, true); - serializer.startTag(null, TAG_PERMISSION_BACKUP); + AtomicReference<byte[]> backup = new AtomicReference<>(); + mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup( + UserHandle.of(userId), mContext.getMainExecutor(), (b) -> { + synchronized (backup) { + backup.set(b); + backup.notifyAll(); + } + }); - synchronized (mPackages) { - serializeRuntimePermissionGrantsLPr(serializer, userId); - } + long start = System.currentTimeMillis(); + synchronized (backup) { + while (backup.get() == null) { + long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis(); + if (timeLeft <= 0) { + return null; + } - serializer.endTag(null, TAG_PERMISSION_BACKUP); - serializer.endDocument(); - serializer.flush(); - } catch (Exception e) { - if (DEBUG_BACKUP) { - Slog.e(TAG, "Unable to write default apps for backup", e); + try { + backup.wait(timeLeft); + } catch (InterruptedException ignored) { + return null; + } } - return null; } - return dataStream.toByteArray(); + return backup.get(); } @Override @@ -19620,66 +19647,6 @@ public class PackageManagerService extends IPackageManager.Stub } @GuardedBy("mPackages") - private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId) - throws IOException { - serializer.startTag(null, TAG_ALL_GRANTS); - - final int N = mSettings.mPackages.size(); - for (int i = 0; i < N; i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); - boolean pkgGrantsKnown = false; - - PermissionsState packagePerms = ps.getPermissionsState(); - - for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) { - final int grantFlags = state.getFlags(); - // only look at grants that are not system/policy fixed - if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) { - final boolean isGranted = state.isGranted(); - // And only back up the user-twiddled state bits - if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) { - final String packageName = mSettings.mPackages.keyAt(i); - if (!pkgGrantsKnown) { - serializer.startTag(null, TAG_GRANT); - serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); - pkgGrantsKnown = true; - } - - final boolean userSet = - (grantFlags & FLAG_PERMISSION_USER_SET) != 0; - final boolean userFixed = - (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0; - final boolean revoke = - (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0; - - serializer.startTag(null, TAG_PERMISSION); - serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName()); - if (isGranted) { - serializer.attribute(null, ATTR_IS_GRANTED, "true"); - } - if (userSet) { - serializer.attribute(null, ATTR_USER_SET, "true"); - } - if (userFixed) { - serializer.attribute(null, ATTR_USER_FIXED, "true"); - } - if (revoke) { - serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true"); - } - serializer.endTag(null, TAG_PERMISSION); - } - } - } - - if (pkgGrantsKnown) { - serializer.endTag(null, TAG_GRANT); - } - } - - serializer.endTag(null, TAG_ALL_GRANTS); - } - - @GuardedBy("mPackages") private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId) throws XmlPullParserException, IOException { String pkgName = null; @@ -19861,6 +19828,14 @@ public class PackageManagerService extends IPackageManager.Stub .setPackage(launcherComponent.getPackageName()); mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid)); } + // TODO(b/122900055) Change/Remove this and replace with new permission role. + if (mAppPredictionServicePackage != null) { + Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED) + .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) + .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) + .setPackage(mAppPredictionServicePackage); + mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid)); + } } } @@ -20013,6 +19988,20 @@ public class PackageManagerService extends IPackageManager.Stub return mContext.getString(R.string.config_defaultWellbeingPackage); } + private String getAppPredictionServicePackageName() { + String flattenedAppPredictionServiceComponentName = + mContext.getString(R.string.config_defaultAppPredictionService); + if (flattenedAppPredictionServiceComponentName == null) { + return null; + } + ComponentName appPredictionServiceComponentName = + ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName); + if (appPredictionServiceComponentName == null) { + return null; + } + return appPredictionServiceComponentName.getPackageName(); + } + @Override public void setApplicationEnabledSetting(String appPackageName, int newState, int flags, int userId, String callingPackage) { @@ -23175,6 +23164,8 @@ public class PackageManagerService extends IPackageManager.Stub return mWellbeingPackage; case PackageManagerInternal.PACKAGE_DOCUMENTER: return mDocumenterPackage; + case PackageManagerInternal.PACKAGE_CONFIGURATOR: + return mConfiguratorPackage; } return null; } @@ -23191,6 +23182,11 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public void setLocationExtraPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setLocationExtraPackagesProvider(provider); + } + + @Override public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 95da2091828d..b0f232607d88 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -2790,13 +2790,13 @@ public final class Settings { // dataPath - path to package's data path // seinfo - seinfo label for the app (assigned at install time) // gids - supplementary gids this app launches with + // profileableFromShellFlag - 0 or 1 if the package is profileable from shell. // // NOTE: We prefer not to expose all ApplicationInfo flags for now. // // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: - // frameworks/base/libs/packagelistparser - // system/core/run-as/run-as.c + // system/core/libpackagelistparser // sb.setLength(0); sb.append(ai.packageName); @@ -2816,6 +2816,8 @@ public final class Settings { } else { sb.append("none"); } + sb.append(" "); + sb.append(ai.isProfileableByShell() ? "1" : "0"); sb.append("\n"); writer.append(sb); } diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java index 68a755b382ca..78fa82c6bcdd 100644 --- a/services/core/java/com/android/server/pm/dex/DexLogger.java +++ b/services/core/java/com/android/server/pm/dex/DexLogger.java @@ -28,7 +28,6 @@ import android.util.PackageUtils; import android.util.Slog; import android.util.SparseArray; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; @@ -53,21 +52,18 @@ public class DexLogger { private final IPackageManager mPackageManager; private final PackageDynamicCodeLoading mPackageDynamicCodeLoading; - private final Object mInstallLock; - @GuardedBy("mInstallLock") private final Installer mInstaller; - public DexLogger(IPackageManager pms, Installer installer, Object installLock) { - this(pms, installer, installLock, new PackageDynamicCodeLoading()); + public DexLogger(IPackageManager pms, Installer installer) { + this(pms, installer, new PackageDynamicCodeLoading()); } @VisibleForTesting - DexLogger(IPackageManager pms, Installer installer, Object installLock, + DexLogger(IPackageManager pms, Installer installer, PackageDynamicCodeLoading packageDynamicCodeLoading) { mPackageManager = pms; mPackageDynamicCodeLoading = packageDynamicCodeLoading; mInstaller = installer; - mInstallLock = installLock; } public Set<String> getAllPackagesWithDynamicCodeLoading() { @@ -131,14 +127,16 @@ public class DexLogger { } byte[] hash = null; - synchronized (mInstallLock) { - try { - hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid, - appInfo.volumeUuid, storageFlags); - } catch (InstallerException e) { - Slog.e(TAG, "Got InstallerException when hashing file " + filePath - + ": " + e.getMessage()); - } + try { + // Note that we do not take the install lock here. Hashing should never interfere + // with app update/compilation/removal. We may get anomalous results if a file + // changes while we hash it, but that can happen anyway and is harmless for our + // purposes. + hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid, + appInfo.volumeUuid, storageFlags); + } catch (InstallerException e) { + Slog.e(TAG, "Got InstallerException when hashing file " + filePath + + ": " + e.getMessage()); } String fileName = new File(filePath).getName(); 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 e57d9d7ab61c..b54683673e7b 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -129,7 +129,7 @@ public class DexManager { mPackageDexOptimizer = pdo; mInstaller = installer; mInstallLock = installLock; - mDexLogger = new DexLogger(pms, installer, installLock); + mDexLogger = new DexLogger(pms, installer); } public DexLogger getDexLogger() { 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 3a49412357d8..17f83479a3b1 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -247,6 +247,10 @@ public final class BasePermission { public boolean isDocumenter() { return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0; } + public boolean isConfigurator() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) + != 0; + } public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { if (!origPackageName.equals(sourcePackageName)) { diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 789664d82332..ceaf69d72108 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -225,6 +225,7 @@ public final class DefaultPermissionGrantPolicy { private final Handler mHandler; private PackagesProvider mLocationPackagesProvider; + private PackagesProvider mLocationExtraPackagesProvider; private PackagesProvider mVoiceInteractionPackagesProvider; private PackagesProvider mSmsAppPackagesProvider; private PackagesProvider mDialerAppPackagesProvider; @@ -270,6 +271,13 @@ public final class DefaultPermissionGrantPolicy { } } + /** Sets the provider for loction extra packages. */ + public void setLocationExtraPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mLocationExtraPackagesProvider = provider; + } + } + public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { synchronized (mLock) { mVoiceInteractionPackagesProvider = provider; @@ -403,6 +411,7 @@ public final class DefaultPermissionGrantPolicy { Log.i(TAG, "Granting permissions to default platform handlers for user " + userId); final PackagesProvider locationPackagesProvider; + final PackagesProvider locationExtraPackagesProvider; final PackagesProvider voiceInteractionPackagesProvider; final PackagesProvider smsAppPackagesProvider; final PackagesProvider dialerAppPackagesProvider; @@ -412,6 +421,7 @@ public final class DefaultPermissionGrantPolicy { synchronized (mLock) { locationPackagesProvider = mLocationPackagesProvider; + locationExtraPackagesProvider = mLocationExtraPackagesProvider; voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider; smsAppPackagesProvider = mSmsAppPackagesProvider; dialerAppPackagesProvider = mDialerAppPackagesProvider; @@ -424,6 +434,8 @@ public final class DefaultPermissionGrantPolicy { ? voiceInteractionPackagesProvider.getPackages(userId) : null; String[] locationPackageNames = (locationPackagesProvider != null) ? locationPackagesProvider.getPackages(userId) : null; + String[] locationExtraPackageNames = (locationExtraPackagesProvider != null) + ? locationExtraPackagesProvider.getPackages(userId) : null; String[] smsAppPackageNames = (smsAppPackagesProvider != null) ? smsAppPackagesProvider.getPackages(userId) : null; String[] dialerAppPackageNames = (dialerAppPackagesProvider != null) @@ -638,6 +650,12 @@ public final class DefaultPermissionGrantPolicy { LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS); } } + if (locationExtraPackageNames != null) { + // Also grant location permission to location extra packages. + for (String packageName : locationExtraPackageNames) { + grantPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS); + } + } // Music Intent musicIntent = new Intent(Intent.ACTION_VIEW) 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 93964cb09ae6..30b5e49bc3fd 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1640,6 +1640,13 @@ public class PermissionManagerService { // Special permissions for the system default text classifier. allowed = true; } + if (!allowed && bp.isConfigurator() + && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName( + PackageManagerInternal.PACKAGE_CONFIGURATOR, + UserHandle.USER_SYSTEM))) { + // Special permissions for the device configurator. + allowed = true; + } if (!allowed && bp.isWellbeing() && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName( PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2060aef37044..6b111a0fa4fb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -84,8 +84,10 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs + .CAMERA_LENS_COVER_ABSENT; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs + .CAMERA_LENS_UNCOVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; @@ -478,6 +480,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mShortPressOnSleepBehavior; int mShortPressOnWindowBehavior; boolean mHasSoftInput = false; + boolean mHapticTextHandleEnabled; boolean mUseTvRouting; int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; @@ -565,6 +568,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mScreenshotChordPowerKeyTriggered; private long mScreenshotChordPowerKeyTime; + private static final long MOVING_DISPLAY_TO_TOP_DURATION_MILLIS = 10; + private volatile boolean mMovingDisplayToTopKeyTriggered; + private volatile long mMovingDisplayToTopKeyTime; + // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up private int mRingerToggleChord = VOLUME_HUSH_OFF; @@ -604,7 +611,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mAodShowing; private boolean mPerDisplayFocusEnabled = false; - private int mTopFocusedDisplayId = INVALID_DISPLAY; + private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; private static final int MSG_ENABLE_POINTER_LOCATION = 1; private static final int MSG_DISABLE_POINTER_LOCATION = 2; @@ -632,6 +639,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_POWER_VERY_LONG_PRESS = 25; private static final int MSG_NOTIFY_USER_ACTIVITY = 26; private static final int MSG_RINGER_TOGGLE_CHORD = 27; + private static final int MSG_MOVE_DISPLAY_TO_TOP = 28; private class PolicyHandler extends Handler { @Override @@ -727,6 +735,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_RINGER_TOGGLE_CHORD: handleRingerChordGesture(); break; + case MSG_MOVE_DISPLAY_TO_TOP: + mWindowManagerFuncs.moveDisplayToTop(msg.arg1); + mMovingDisplayToTopKeyTriggered = false; + break; } } } @@ -808,6 +820,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + private Runnable mPossibleVeryLongPressReboot = new Runnable() { + @Override + public void run() { + mActivityManagerInternal.prepareForPossibleShutdown(); + } + }; + private void handleRingerChordGesture() { if (mRingerToggleChord == VOLUME_HUSH_OFF) { return; @@ -953,6 +972,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Inform the StatusBar; but do not allow it to consume the event. sendSystemKeyToStatusBarAsync(event.getKeyCode()); + schedulePossibleVeryLongPressReboot(); + // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered @@ -1056,6 +1077,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (hasVeryLongPressOnPowerBehavior()) { mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); } + cancelPossibleVeryLongPressReboot(); } private void cancelPendingBackKeyAction() { @@ -1815,6 +1837,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); + mHapticTextHandleEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableHapticTextHandle); + mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION; mHandleVolumeKeysInWM = mContext.getResources().getBoolean( @@ -2558,12 +2583,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int eventDisplayId = event.getDisplayId(); if (result == 0 && !mPerDisplayFocusEnabled && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) { - // Someone tries to send a key event to a display which doesn't have a focused window. - // We drop the event here, or it will cause ANR. - // TODO (b/121057974): The user may be confused about why the key doesn't work, so we - // may need to deal with this problem. - Slog.i(TAG, "Dropping this event targeting display #" + eventDisplayId - + " because the focus is on display #" + mTopFocusedDisplayId); + // An event is targeting a non-focused display. Try to move the display to top so that + // it can become the focused display to interact with the user. + final long eventDownTime = event.getDownTime(); + if (mMovingDisplayToTopKeyTime < eventDownTime) { + // We have not handled this event yet. Move the display to top, and then tell + // dispatcher to try again later. + mMovingDisplayToTopKeyTime = eventDownTime; + mMovingDisplayToTopKeyTriggered = true; + mHandler.sendMessage( + mHandler.obtainMessage(MSG_MOVE_DISPLAY_TO_TOP, eventDisplayId, 0)); + return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS; + } else if (mMovingDisplayToTopKeyTriggered) { + // The message has not been handled yet. Tell dispatcher to try again later. + return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS; + } + // The target display is still not the top focused display. Drop the event because the + // display may not contain any window which can receive keys. + Slog.w(TAG, "Dropping key targeting non-focused display #" + eventDisplayId + + " keyCode=" + KeyEvent.keyCodeToString(event.getKeyCode())); return -1; } return result; @@ -4901,6 +4939,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void schedulePossibleVeryLongPressReboot() { + mHandler.removeCallbacks(mPossibleVeryLongPressReboot); + mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout); + } + + private void cancelPossibleVeryLongPressReboot() { + mHandler.removeCallbacks(mPossibleVeryLongPressReboot); + } + // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. private void updateScreenOffSleepToken(boolean acquire) { if (acquire) { @@ -5158,8 +5205,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case HapticFeedbackConstants.CLOCK_TICK: case HapticFeedbackConstants.CONTEXT_CLICK: return VibrationEffect.get(VibrationEffect.EFFECT_TICK); - case HapticFeedbackConstants.KEYBOARD_RELEASE: case HapticFeedbackConstants.TEXT_HANDLE_MOVE: + if (!mHapticTextHandleEnabled) { + return null; + } + case HapticFeedbackConstants.KEYBOARD_RELEASE: case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: case HapticFeedbackConstants.ENTRY_BUMP: case HapticFeedbackConstants.DRAG_CROSSING: @@ -5324,11 +5374,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup); pw.print(prefix); pw.print("mHasSoftInput="); pw.print(mHasSoftInput); - pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed); + pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled); + pw.print(prefix); + pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed); + pw.print(" mIncallPowerBehavior="); + pw.println(incallPowerBehaviorToString(mIncallPowerBehavior)); pw.print(prefix); - pw.print("mIncallPowerBehavior="); - pw.print(incallPowerBehaviorToString(mIncallPowerBehavior)); - pw.print(" mIncallBackBehavior="); + pw.print("mIncallBackBehavior="); pw.print(incallBackBehaviorToString(mIncallBackBehavior)); pw.print(" mEndcallBehavior="); pw.println(endcallBehaviorToString(mEndcallBehavior)); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 3da325c55b32..c37254b22ea5 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -634,6 +634,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * Notifies window manager that user is switched. */ void onUserSwitched(); + + /** + * Hint to window manager that the user is interacting with a display that should be treated + * as the top display. + */ + void moveDisplayToTop(int displayId); } /** 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 45c975b26956..055c941f8b0a 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -62,6 +62,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. if (result == null) { Collection<SmsApplication.SmsApplicationData> applications = SmsApplication.getApplicationCollectionAsUser(mContext, userId); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index c0ec3672c665..5516b234925b 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -229,9 +229,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C // Any role for which we have a record are already migrated RoleUserState userState = getOrCreateUserState(userId); if (!userState.isRoleAvailable(role)) { - userState.addRoleName(role); List<String> roleHolders = mLegacyRoleResolver.getRoleHolders(role, userId); + if (roleHolders.isEmpty()) { + return; + } Slog.i(LOG_TAG, "Migrating " + role + ", legacy holders: " + roleHolders); + userState.addRoleName(role); int size = roleHolders.size(); for (int i = 0; i < size; i++) { userState.addRoleHolder(role, roleHolders.get(i)); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 30aa5280eac6..4e71a054fa80 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -43,6 +43,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.fingerprint.FingerprintManager; import android.net.ConnectivityManager; +import android.net.INetworkStatsService; import android.net.Network; import android.net.NetworkRequest; import android.net.NetworkStats; @@ -90,7 +91,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.IProcessStats; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.net.NetworkStatsFactory; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BinderCallsStats.ExportedCallStat; @@ -210,6 +210,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final Context mContext; private final AlarmManager mAlarmManager; + private final INetworkStatsService mNetworkStatsService; @GuardedBy("sStatsdLock") private static IStatsManager sStatsd; private static final Object sStatsdLock = new Object(); @@ -257,6 +258,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { super(); mContext = context; mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + mNetworkStatsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); mBaseDir.mkdirs(); mAppUpdateReceiver = new AppUpdateReceiver(); mUserUpdateReceiver = new BroadcastReceiver() { @@ -788,14 +791,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } // Combine all the metrics per Uid into one record. - NetworkStats stats = - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null) - .groupedByUid(); + NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid(); addNetworkStats(tagId, pulledData, stats, false); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for wifi bytes has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -812,12 +815,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } NetworkStats stats = rollupNetworkStatsByFGBG( - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null)); + mNetworkStatsService.getDetailedUidStats(ifaces)); addNetworkStats(tagId, pulledData, stats, true); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -834,14 +839,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } // Combine all the metrics per Uid into one record. - NetworkStats stats = - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null) - .groupedByUid(); + NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid(); addNetworkStats(tagId, pulledData, stats, false); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for mobile bytes has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -874,12 +879,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } NetworkStats stats = rollupNetworkStatsByFGBG( - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null)); + mNetworkStatsService.getDetailedUidStats(ifaces)); addNetworkStats(tagId, pulledData, stats, true); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -2058,6 +2065,17 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { mContext.unregisterReceiver(mShutdownEventReceiver); cancelAnomalyAlarm(); cancelPullingAlarm(); + + BinderCallsStatsService.Internal binderStats = + LocalServices.getService(BinderCallsStatsService.Internal.class); + if (binderStats != null) { + binderStats.reset(); + } + + LooperStats looperStats = LocalServices.getService(LooperStats.class); + if (looperStats != null) { + looperStats.reset(); + } } @Override diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index 1f638c7f2e5d..65d66f44b5dd 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -278,12 +278,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } // Since positionChildAt() is called during the creation process of pinned stacks, - // ActivityStack#getWindowContainerController() can be null. In this special case, + // ActivityStack#getStack() can be null. In this special case, // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(), // we don't have to call WindowContainerController#positionChildAt() here. - if (stack.getWindowContainerController() != null && mDisplayContent != null) { + if (stack.getTaskStack() != null && mDisplayContent != null) { mDisplayContent.positionStackAt(insertPosition, - stack.getWindowContainerController().mContainer, includingParents); + stack.getTaskStack(), includingParents); } if (!wasContained) { stack.setParent(this); @@ -450,13 +450,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> @VisibleForTesting <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop) { - if (windowingMode == WINDOWING_MODE_PINNED) { - return (T) new PinnedActivityStack(this, stackId, - mRootActivityContainer.mStackSupervisor, onTop); + if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) { + throw new IllegalArgumentException("Stack with windowing mode cannot with non standard " + + "activity type."); } return (T) new ActivityStack(this, stackId, - mRootActivityContainer.mStackSupervisor, windowingMode, activityType, - onTop); + mRootActivityContainer.mStackSupervisor, windowingMode, activityType, onTop); } /** @@ -626,6 +625,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> return; } + // Collect the stacks that are necessary to be removed instead of performing the removal + // by looping mStacks, so that we don't miss any stacks after the stack size changed or + // stacks reordered. + final ArrayList<ActivityStack> stacks = new ArrayList<>(); for (int j = windowingModes.length - 1 ; j >= 0; --j) { final int windowingMode = windowingModes[j]; for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -636,9 +639,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (stack.getWindowingMode() != windowingMode) { continue; } - mRootActivityContainer.mStackSupervisor.removeStack(stack); + stacks.add(stack); } } + + for (int i = stacks.size() - 1; i >= 0; --i) { + mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i)); + } } void removeStacksWithActivityTypes(int... activityTypes) { @@ -646,15 +653,23 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> return; } + // Collect the stacks that are necessary to be removed instead of performing the removal + // by looping mStacks, so that we don't miss any stacks after the stack size changed or + // stacks reordered. + final ArrayList<ActivityStack> stacks = new ArrayList<>(); for (int j = activityTypes.length - 1 ; j >= 0; --j) { final int activityType = activityTypes[j]; for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = mStacks.get(i); if (stack.getActivityType() == activityType) { - mRootActivityContainer.mStackSupervisor.removeStack(stack); + stacks.add(stack); } } } + + for (int i = stacks.size() - 1; i >= 0; --i) { + mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i)); + } } void onStackWindowingModeChanged(ActivityStack stack) { @@ -1019,8 +1034,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> return mSplitScreenPrimaryStack != null; } - PinnedActivityStack getPinnedStack() { - return (PinnedActivityStack) mPinnedStack; + ActivityStack getPinnedStack() { + return mPinnedStack; } boolean hasPinnedStack() { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9f8af5048da2..b8634d88319a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2508,6 +2508,13 @@ final class ActivityRecord extends ConfigurationContainer { final IBinder binder = (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null; mAppWindowToken.setOrientation(requestedOrientation, binder, this); + + // Push the new configuration to the requested app in case where it's not pushed, e.g. when + // the request is handled at task level with letterbox. + if (!getMergedOverrideConfiguration().equals( + mLastReportedConfiguration.getMergedConfiguration())) { + ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); + } } int getOrientation() { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 4d7de9058105..891c3da90b93 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -114,6 +114,7 @@ import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityController; +import android.app.RemoteAction; import android.app.ResultInfo; import android.app.WindowConfiguration.ActivityType; import android.app.WindowConfiguration.WindowingMode; @@ -173,8 +174,7 @@ import java.util.Set; /** * State and management of a single stack of activities. */ -class ActivityStack<T extends StackWindowController> extends ConfigurationContainer - implements StackWindowListener { +class ActivityStack extends ConfigurationContainer { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM; private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_APP = TAG + POSTFIX_APP; @@ -297,8 +297,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2; final ActivityTaskManagerService mService; - private final WindowManagerService mWindowManager; - T mWindowContainerController; + final WindowManagerService mWindowManager; /** * The back history of all previous (and possibly still @@ -397,6 +396,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5; static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6; + // TODO: remove after unification. + TaskStack mTaskStack; + private static class ScheduleDestroyArgs { final WindowProcessController mOwner; final String mReason; @@ -495,21 +497,30 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // stacks on a wrong display. mDisplayId = display.mDisplayId; setActivityType(activityType); - mWindowContainerController = createStackWindowController(display.mDisplayId, onTop, - mTmpRect2); + createTaskStack(display.mDisplayId, onTop, mTmpRect2); setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, true /* creating */); display.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM); } - T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { - return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds, - mRootActivityContainer.mWindowManager); + void createTaskStack(int displayId, boolean onTop, Rect outBounds) { + final DisplayContent dc = mWindowManager.mRoot.getDisplayContent(displayId); + if (dc == null) { + throw new IllegalArgumentException("Trying to add stackId=" + mStackId + + " to unknown displayId=" + displayId); + } + mTaskStack = new TaskStack(mWindowManager, mStackId, this); + dc.setStackOnDisplay(mStackId, onTop, mTaskStack); + if (mTaskStack.matchParentBounds()) { + outBounds.setEmpty(); + } else { + mTaskStack.getRawBounds(outBounds); + } } - T getWindowContainerController() { - return mWindowContainerController; + TaskStack getTaskStack() { + return mTaskStack; } /** @@ -553,6 +564,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (display == null) { return; } + if (getTaskStack() == null) { + return; + } // Update bounds if applicable boolean hasNewOverrideBounds = false; @@ -560,8 +574,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_PINNED) { // Pinned calculation already includes rotation mTmpRect2.set(mTmpRect); - hasNewOverrideBounds = getWindowContainerController().mContainer - .calculatePinnedBoundsForConfigChange(mTmpRect2); + hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(mTmpRect2); } else { final int newRotation = getWindowConfiguration().getRotation(); if (!matchParentBounds()) { @@ -588,7 +601,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { mTmpRect2.set(mTmpRect); - getWindowContainerController().mContainer + getTaskStack() .calculateDockedBoundsForConfigChange(newParentConfig, mTmpRect2); hasNewOverrideBounds = true; } @@ -786,7 +799,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mTmpRect2.setEmpty(); if (windowingMode != WINDOWING_MODE_FULLSCREEN) { - mWindowContainerController.getRawBounds(mTmpRect2); + if (mTaskStack.matchParentBounds()) { + mTmpRect2.setEmpty(); + } else { + mTaskStack.getRawBounds(mTmpRect2); + } } if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) { @@ -843,7 +860,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Reparent the window container before we try to update the position when adding it to // the new display below mTmpRect2.setEmpty(); - mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop); + if (mTaskStack == null) { + // TODO: Remove after unification. + Log.w(TAG, "Task stack is not valid when reparenting."); + } else { + mTaskStack.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop); + } setBounds(mTmpRect2.isEmpty() ? null : mTmpRect2); activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM); if (!displayRemoved) { @@ -876,8 +898,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** Removes the stack completely. Also calls WindowManager to do the same on its side. */ void remove() { removeFromDisplay(); - mWindowContainerController.removeContainer(); - mWindowContainerController = null; + if (mTaskStack != null) { + mTaskStack.removeIfPossible(); + mTaskStack = null; + } onParentChanged(); } @@ -890,26 +914,35 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ void getStackDockedModeBounds(Rect dockedBounds, Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) { - mWindowContainerController.getStackDockedModeBounds(getParent().getConfiguration(), - dockedBounds, currentTempTaskBounds, - outStackBounds, outTempTaskBounds); + if (mTaskStack != null) { + mTaskStack.getStackDockedModeBoundsLocked(getParent().getConfiguration(), dockedBounds, + currentTempTaskBounds, outStackBounds, outTempTaskBounds); + } else { + outStackBounds.setEmpty(); + outTempTaskBounds.setEmpty(); + } } void prepareFreezingTaskBounds() { - mWindowContainerController.prepareFreezingTaskBounds(); + if (mTaskStack != null) { + // TODO: This cannot be false after unification. + mTaskStack.prepareFreezingTaskBounds(); + } } void getWindowContainerBounds(Rect outBounds) { - if (mWindowContainerController != null) { - mWindowContainerController.getBounds(outBounds); + if (mTaskStack != null) { + mTaskStack.getBounds(outBounds); return; } outBounds.setEmpty(); } void positionChildWindowContainerAtTop(TaskRecord child) { - mWindowContainerController.positionChildAtTop(child.getTask(), - true /* includingParents */); + if (mTaskStack != null) { + // TODO: Remove after unification. This cannot be false after that. + mTaskStack.positionChildAtTop(child.getTask(), true /* includingParents */); + } } void positionChildWindowContainerAtBottom(TaskRecord child) { @@ -918,14 +951,27 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // task to bottom, the next focusable stack on the same display should be focused. final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack( child.getStack(), true /* ignoreCurrent */); - mWindowContainerController.positionChildAtBottom(child.getTask(), - nextFocusableStack == null /* includingParents */); + if (mTaskStack != null) { + // TODO: Remove after unification. This cannot be false after that. + mTaskStack.positionChildAtBottom(child.getTask(), + nextFocusableStack == null /* includingParents */); + } } /** * Returns whether to defer the scheduling of the multi-window mode. */ boolean deferScheduleMultiWindowModeChanged() { + if (inPinnedWindowingMode()) { + // For the pinned stack, the deferring of the multi-window mode changed is tied to the + // transition animation into picture-in-picture, and is called once the animation + // completes, or is interrupted in a way that would leave the stack in a non-fullscreen + // state. + // @see BoundsAnimationController + // @see BoundsAnimationControllerTests + if (getTaskStack() == null) return false; + return getTaskStack().deferScheduleMultiWindowModeChanged(); + } return false; } @@ -2994,7 +3040,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai position = getAdjustedPositionForTask(task, position, null /* starting */); mTaskHistory.remove(task); mTaskHistory.add(position, task); - mWindowContainerController.positionChildAt(task.getTask(), position); + if (mTaskStack != null) { + // TODO: this could not be false after unification. + mTaskStack.positionChildAt(task.getTask(), position); + } updateTaskMovement(task, true); } @@ -4909,8 +4958,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } // TODO: Figure-out a way to consolidate with resize() method below. - @Override - public void requestResize(Rect bounds) { + void requestResize(Rect bounds) { mService.resizeStack(mStackId, bounds, true /* allowResizeInDockedMode */, false /* preserveWindows */, false /* animate */, -1 /* animationDuration */); @@ -4948,7 +4996,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } void onPipAnimationEndResize() { - mWindowContainerController.onPipAnimationEndResize(); + if (mTaskStack == null) return; + mTaskStack.onPipAnimationEndResize(); } @@ -5494,6 +5543,65 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } + + Rect getDefaultPictureInPictureBounds(float aspectRatio) { + if (getTaskStack() == null) return null; + return getTaskStack().getPictureInPictureBounds(aspectRatio, null /* currentStackBounds */); + } + + void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration, + boolean fromFullscreen) { + if (!inPinnedWindowingMode()) return; + if (skipResizeAnimation(toBounds == null /* toFullscreen */)) { + mService.moveTasksToFullscreenStack(mStackId, true /* onTop */); + } else { + if (getTaskStack() == null) return; + getTaskStack().animateResizePinnedStack(toBounds, sourceHintBounds, + animationDuration, fromFullscreen); + } + } + + private boolean skipResizeAnimation(boolean toFullscreen) { + if (!toFullscreen) { + return false; + } + final Configuration parentConfig = getParent().getConfiguration(); + final ActivityRecord top = topRunningNonOverlayTaskActivity(); + return top != null && !top.isConfigurationCompatible(parentConfig); + } + + void setPictureInPictureAspectRatio(float aspectRatio) { + if (getTaskStack() == null) return; + getTaskStack().setPictureInPictureAspectRatio(aspectRatio); + } + + void setPictureInPictureActions(List<RemoteAction> actions) { + if (getTaskStack() == null) return; + getTaskStack().setPictureInPictureActions(actions); + } + + boolean isAnimatingBoundsToFullscreen() { + if (getTaskStack() == null) return false; + return getTaskStack().isAnimatingBoundsToFullscreen(); + } + + public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, + boolean forceUpdate) { + // It is guaranteed that the activities requiring the update will be in the pinned stack at + // this point (either reparented before the animation into PiP, or before reparenting after + // the animation out of PiP) + synchronized (mService.mGlobalLock) { + if (!isAttached()) { + return; + } + ArrayList<TaskRecord> tasks = getAllTasks(); + for (int i = 0; i < tasks.size(); i++) { + mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds, + forceUpdate); + } + } + } + public int getStackId() { return mStackId; } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index a50ae8431cb0..3a288ca5560d 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1674,8 +1674,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) { - // TODO(multi-display): Pinned stack display should be passed in. - final PinnedActivityStack stack = + // TODO(multi-display): The display containing the stack should be passed in. + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getPinnedStack(); if (stack == null) { Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); @@ -1686,7 +1686,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting // for the AMS lock to be freed. So check and make sure these bounds are still good. - final PinnedStackWindowController stackController = stack.getWindowContainerController(); + final TaskStack stackController = stack.getTaskStack(); if (stackController.pinnedStackResizeDisallowed()) { return; } @@ -1730,15 +1730,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { * invisible as well and added to the stopping list. After which we process the * stopping list by handling the idle. */ - final PinnedActivityStack pinnedStack = (PinnedActivityStack) stack; - pinnedStack.mForceHidden = true; - pinnedStack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); - pinnedStack.mForceHidden = false; + stack.mForceHidden = true; + stack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); + stack.mForceHidden = false; activityIdleInternalLocked(null, false /* fromTimeout */, true /* processPausingActivites */, null /* configuration */); // Move all the tasks to the bottom of the fullscreen stack - moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP); + moveTasksToFullscreenStackLocked(stack, !ON_TOP); } else { for (int i = tasks.size() - 1; i >= 0; i--) { removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */, @@ -2642,6 +2641,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { try { mService.moveTaskToFrontLocked(task.taskId, 0, options, true /* fromRecents */); + // Apply options to prevent pendingOptions be taken by client to make sure + // the override pending app transition will be applied immediately. + targetActivity.applyOptionsLocked(); } finally { mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, targetActivity); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index ef3c8d5257fb..916baa08fcbc 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -749,7 +749,7 @@ class ActivityStarter { if (!abort) { abort |= shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, callerApp, originatingPendingIntent, - allowBackgroundActivityStart); + allowBackgroundActivityStart, intent); } // Merge the two options bundles, while realCallerOptions takes precedence. @@ -898,7 +898,8 @@ class ActivityStarter { private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, final String callingPackage, int realCallingUid, WindowProcessController callerApp, - PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { + PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart, + Intent intent) { if (mService.isBackgroundActivityStartsEnabled()) { return false; } @@ -911,19 +912,24 @@ class ActivityStarter { return false; } // don't abort if the callingUid is in the foreground or is a persistent system process - if (isUidForeground(callingUid) || isUidPersistentSystemProcess(callingUid)) { + final boolean isCallingUidForeground = isUidForeground(callingUid); + final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess( + callingUid); + if (isCallingUidForeground || isCallingUidPersistentSystemProcess) { return false; } // take realCallingUid into consideration + final boolean isRealCallingUidForeground = isUidForeground(realCallingUid); + final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess( + realCallingUid); if (realCallingUid != callingUid) { // don't abort if the realCallingUid is in the foreground and callingUid isn't - if (isUidForeground(realCallingUid)) { + if (isRealCallingUidForeground) { return false; } // if the realCallingUid is a persistent system process, abort if the IntentSender // wasn't whitelisted to start an activity - if (isUidPersistentSystemProcess(realCallingUid) && (originatingPendingIntent != null) - && allowBackgroundActivityStart) { + if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) { return false; } } @@ -941,6 +947,18 @@ class ActivityStarter { return false; } // anything that has fallen through will currently be aborted + Slog.w(TAG, "Blocking background activity start [callingPackage: " + callingPackage + + "; callingUid: " + callingUid + + "; isCallingUidForeground: " + isCallingUidForeground + + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess + + "; realCallingUid: " + realCallingUid + + "; isRealCallingUidForeground: " + isRealCallingUidForeground + + "; isRealCallingUidPersistentSystemProcess: " + + isRealCallingUidPersistentSystemProcess + + "; originatingPendingIntent: " + originatingPendingIntent + + "; isBgStartWhitelisted: " + allowBackgroundActivityStart + + "; intent: " + intent + + "]"); // TODO: remove this toast after feature development is done mService.mUiHandler.post(() -> { Toast.makeText(mService.mContext, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e82e7480740c..6a495d4e9683 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -82,13 +82,10 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HEA import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC; import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY; import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC; -import static com.android.server.am.ActivityManagerServiceDumpProcessesProto - .PREVIOUS_PROC_VISIBLE_TIME_MS; +import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS; import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES; -import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage - .MODE; -import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage - .PACKAGE; +import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE; +import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; @@ -210,6 +207,7 @@ import android.os.storage.StorageManager; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; +import android.sysprop.DisplayProperties; import android.telecom.TelecomManager; import android.text.TextUtils; import android.text.format.Time; @@ -245,7 +243,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.appop.AppOpsService; import com.android.server.AttributeCache; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -261,6 +258,7 @@ import com.android.server.am.EventLogTags; import com.android.server.am.PendingIntentController; import com.android.server.am.PendingIntentRecord; import com.android.server.am.UserState; +import com.android.server.appop.AppOpsService; import com.android.server.firewall.IntentFirewall; import com.android.server.pm.UserManagerService; import com.android.server.uri.UriGrantsManagerInternal; @@ -694,7 +692,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); // Transfer any global setting for forcing RTL layout, into a System Property - SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0"); + DisplayProperties.debug_force_rtl(forceRtl); final Configuration configuration = new Configuration(); Settings.System.getConfiguration(resolver, configuration); @@ -2407,7 +2405,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { if (animate) { - final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; @@ -3712,7 +3710,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final PinnedActivityStack stack = + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getPinnedStack(); if (stack == null) { Slog.w(TAG, "dismissPip: pinned stack not found."); @@ -3834,9 +3832,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // If we are animating to fullscreen then we have already dispatched the PIP mode // changed, so we should reflect that check here as well. - final PinnedActivityStack stack = r.getActivityStack(); - final PinnedStackWindowController windowController = stack.getWindowContainerController(); - return !windowController.mContainer.isAnimatingBoundsToFullscreen(); + final TaskStack taskStack = r.getActivityStack().getTaskStack(); + return !taskStack.isAnimatingBoundsToFullscreen(); } @Override @@ -3870,7 +3867,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.pictureInPictureArgs.getSourceRectHint()); mRootActivityContainer.moveActivityToPinnedStack( r, sourceBounds, aspectRatio, "enterPictureInPictureMode"); - final PinnedActivityStack stack = r.getActivityStack(); + final ActivityStack stack = r.getActivityStack(); stack.setPictureInPictureAspectRatio(aspectRatio); stack.setPictureInPictureActions(actions); MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid, @@ -3914,7 +3911,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // If the activity is already in picture-in-picture, update the pinned stack now // if it is not already expanding to fullscreen. Otherwise, the arguments will // be used the next time the activity enters PiP - final PinnedActivityStack stack = r.getActivityStack(); + final ActivityStack stack = r.getActivityStack(); if (!stack.isAnimatingBoundsToFullscreen()) { stack.setPictureInPictureAspectRatio( r.pictureInPictureArgs.getAspectRatio()); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 740d47240bd0..6527ca0e751d 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -328,14 +328,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED; /** - * Flag indicating that the application is receiving an orientation that has different metrics - * than it expected. E.g. Portrait instead of Landscape. - * - * @see #updateRotationUnchecked() - */ - private boolean mAltOrientation = false; - - /** * Orientation forced by some window. If there is no visible window that specifies orientation * it is set to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}. * @@ -1085,10 +1077,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mLastOrientation; } - boolean getAltOrientation() { - return mAltOrientation; - } - int getLastWindowForcedOrientation() { return mLastWindowForcedOrientation; } @@ -1130,15 +1118,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo boolean rotationNeedsUpdate() { final int lastOrientation = getLastOrientation(); final int oldRotation = getRotation(); - final boolean oldAltOrientation = getAltOrientation(); final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation); - final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics( - lastOrientation, rotation); - if (oldRotation == rotation && oldAltOrientation == altOrientation) { - return false; - } - return true; + return oldRotation != rotation; } /** @@ -1160,12 +1142,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, ConfigurationContainer requestingContainer) { + final int previousRotation = mRotation; final Configuration config = updateOrientationFromAppTokens( getRequestedOverrideConfiguration(), freezeDisplayToken, false); - // 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(); + // 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 (config == null) { return handled; } @@ -1334,7 +1318,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int oldRotation = mRotation; final int lastOrientation = mLastOrientation; - final boolean oldAltOrientation = mAltOrientation; final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation); if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id=" + mDisplayId + " based on lastOrientation=" + lastOrientation @@ -1366,35 +1349,26 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final boolean rotateSeamlessly = mayRotateSeamlessly; - // TODO: Implement forced rotation changes. - // Set mAltOrientation to indicate that the application is receiving - // an orientation that has different metrics than it expected. - // eg. Portrait instead of Landscape. - - final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics( - lastOrientation, rotation); - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId + " selected orientation " + lastOrientation + ", got rotation " + rotation + " which has " - + (altOrientation ? "incompatible" : "compatible") + " metrics"); + + " metrics"); - if (oldRotation == rotation && oldAltOrientation == altOrientation) { + if (oldRotation == rotation) { // No change. return false; } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId + " rotation changed to " + rotation - + (altOrientation ? " (alt)" : "") + " from " + oldRotation - + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation); + + " from " + oldRotation + + ", lastOrientation=" + lastOrientation); if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) { mWaitingForConfig = true; } mRotation = rotation; - mAltOrientation = altOrientation; mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, @@ -1536,26 +1510,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private DisplayInfo updateDisplayAndOrientation(int uiMode) { // Use the effective "visual" dimensions based on current rotation final boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270); - final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; - final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; - int dw = realdw; - int dh = realdh; - - if (mAltOrientation) { - if (realdw > realdh) { - // Turn landscape into portrait. - int maxw = (int)(realdh/1.3f); - if (maxw < realdw) { - dw = maxw; - } - } else { - // Turn portrait into landscape. - int maxh = (int)(realdw/1.3f); - if (maxh < realdh) { - dh = maxh; - } - } - } + final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; + final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; // Update application display metrics. final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(mRotation); @@ -2307,13 +2263,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo out.set(mDisplayFrames.mStable); } - TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) { - if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId=" - + mDisplayId); + void setStackOnDisplay(int stackId, boolean onTop, TaskStack stack) { + if (DEBUG_STACK) { + Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId=" + mDisplayId); + } - final TaskStack stack = new TaskStack(mWmService, stackId, controller); mTaskStackContainers.addStackToDisplay(stack, onTop); - return stack; } void moveStackToDisplay(TaskStack stack, boolean onTop) { @@ -4015,7 +3970,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * Adds the stack to this container. - * @see DisplayContent#createStack(int, boolean, StackWindowController) */ void addStackToDisplay(TaskStack stack, boolean onTop) { addStackReferenceIfNeeded(stack); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index bcc7be42333b..bc165dceb544 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -329,15 +329,6 @@ 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; } @@ -685,36 +676,6 @@ public class DisplayRotation { return rotation == mPortraitRotation || rotation == mUpsideDownRotation; } - /** - * Given an orientation constant and a rotation, returns true if the rotation - * has compatible metrics to the requested orientation. For example, if - * the application requested landscape and got seascape, then the rotation - * has compatible metrics; if the application requested portrait and got landscape, - * then the rotation has incompatible metrics; if the application did not specify - * a preference, then anything goes. - * - * @param orientation An orientation constant, such as - * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}. - * @param rotation The rotation to check. - * @return True if the rotation is compatible with the requested orientation. - */ - boolean rotationHasCompatibleMetrics(int orientation, int rotation) { - switch (orientation) { - case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: - case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: - case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: - return isAnyPortrait(rotation); - - case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: - case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: - case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: - return isLandscapeOrSeascape(rotation); - - default: - return true; - } - } - private boolean isValidRotationChoice(final int preferredRotation) { // Determine if the given app orientation is compatible with the provided rotation choice. switch (mCurrentAppOrientation) { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 5f56fe59c1f2..177f244129f4 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -462,22 +462,19 @@ class KeyguardController { mOccluded = false; mDismissingKeyguardActivity = null; - // Only the top activity of the focused stack on each display may control it's - // occluded state. - final ActivityStack focusedStack = display.getFocusedStack(); - if (focusedStack != null) { - final ActivityRecord topDismissing = - focusedStack.getTopDismissingKeyguardActivity(); - mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null - && focusedStack.topRunningActivityLocked() == topDismissing - && controller.canShowWhileOccluded( + final ActivityStack stack = getStackForControllingOccluding(display); + if (stack != null) { + final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity(); + mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null + && stack.topRunningActivityLocked() == topDismissing + && controller.canShowWhileOccluded( true /* dismissKeyguard */, false /* showWhenLocked */)); - if (focusedStack.getTopDismissingKeyguardActivity() != null) { - mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity(); + if (stack.getTopDismissingKeyguardActivity() != null) { + mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity(); } - mOccluded |= controller.mWindowManager.isShowingDream(); } + mOccluded |= controller.mWindowManager.isShowingDream(); // TODO(b/113840485): Handle app transition for individual display, and apply occluded // state change to secondary displays. @@ -492,6 +489,23 @@ class KeyguardController { } } + /** + * Gets the stack used to check the occluded state. + * <p> + * Only the top non-pinned activity of the focusable stack on each display can control its + * occlusion state. + */ + private ActivityStack getStackForControllingOccluding(ActivityDisplay display) { + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (stack != null && stack.isFocusableAndVisible() + && !stack.inPinnedWindowingMode()) { + return stack; + } + } + return null; + } + void dumpStatus(PrintWriter pw, String prefix) { final StringBuilder sb = new StringBuilder(); sb.append(prefix); diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 3062d34780b6..86dc66dbca3b 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -221,7 +221,7 @@ class LaunchParamsPersister { } private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) { - final ActivityStack<?> stack = task.getStack(); + final ActivityStack stack = task.getStack(); final int displayId = stack.mDisplayId; final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId); diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java deleted file mode 100644 index 2a05af4f473c..000000000000 --- a/services/core/java/com/android/server/wm/PinnedActivityStack.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2017 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.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - -import android.app.RemoteAction; -import android.content.res.Configuration; -import android.graphics.Rect; - -import java.util.ArrayList; -import java.util.List; - -/** - * State and management of the pinned stack of activities. - */ -class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> - implements PinnedStackWindowListener { - - PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor, - boolean onTop) { - super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop); - } - - @Override - PinnedStackWindowController createStackWindowController(int displayId, boolean onTop, - Rect outBounds) { - return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds, - mRootActivityContainer.mWindowManager); - } - - Rect getDefaultPictureInPictureBounds(float aspectRatio) { - return getWindowContainerController().getPictureInPictureBounds(aspectRatio, - null /* currentStackBounds */); - } - - void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration, - boolean fromFullscreen) { - if (skipResizeAnimation(toBounds == null /* toFullscreen */)) { - mService.moveTasksToFullscreenStack(mStackId, true /* onTop */); - } else { - getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds, - animationDuration, fromFullscreen); - } - } - - private boolean skipResizeAnimation(boolean toFullscreen) { - if (!toFullscreen) { - return false; - } - final Configuration parentConfig = getParent().getConfiguration(); - final ActivityRecord top = topRunningNonOverlayTaskActivity(); - return top != null && !top.isConfigurationCompatible(parentConfig); - } - - void setPictureInPictureAspectRatio(float aspectRatio) { - getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio); - } - - void setPictureInPictureActions(List<RemoteAction> actions) { - getWindowContainerController().setPictureInPictureActions(actions); - } - - boolean isAnimatingBoundsToFullscreen() { - return getWindowContainerController().mContainer.isAnimatingBoundsToFullscreen(); - } - - /** - * Returns whether to defer the scheduling of the multi-window mode. - */ - boolean deferScheduleMultiWindowModeChanged() { - // For the pinned stack, the deferring of the multi-window mode changed is tied to the - // transition animation into picture-in-picture, and is called once the animation completes, - // or is interrupted in a way that would leave the stack in a non-fullscreen state. - // @see BoundsAnimationController - // @see BoundsAnimationControllerTests - return mWindowContainerController.deferScheduleMultiWindowModeChanged(); - } - - public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, - boolean forceUpdate) { - // It is guaranteed that the activities requiring the update will be in the pinned stack at - // this point (either reparented before the animation into PiP, or before reparenting after - // the animation out of PiP) - synchronized (mService.mGlobalLock) { - if (!isAttached()) { - return; - } - ArrayList<TaskRecord> tasks = getAllTasks(); - for (int i = 0; i < tasks.size(); i++ ) { - mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds, - forceUpdate); - } - } - } -} diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java deleted file mode 100644 index 518e39ba9d58..000000000000 --- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2017 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.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; - -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; -import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; - -import android.app.RemoteAction; -import android.graphics.Rect; - -import java.util.List; - -/** - * Controller for the pinned stack container. See {@link StackWindowController}. - */ -public class PinnedStackWindowController extends StackWindowController { - - private Rect mTmpFromBounds = new Rect(); - private Rect mTmpToBounds = new Rect(); - - public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener, - int displayId, boolean onTop, Rect outBounds, WindowManagerService service) { - super(stackId, listener, displayId, onTop, outBounds, service); - } - - /** - * @return the {@param currentStackBounds} transformed to the give {@param aspectRatio}. If - * {@param currentStackBounds} is null, then the {@param aspectRatio} is applied to the - * default bounds. - */ - public Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) { - synchronized (mGlobalLock) { - if (!mService.mSupportsPictureInPicture || mContainer == null) { - return null; - } - - final DisplayContent displayContent = mContainer.getDisplayContent(); - if (displayContent == null) { - return null; - } - - final PinnedStackController pinnedStackController = - displayContent.getPinnedStackController(); - if (stackBounds == null) { - // Calculate the aspect ratio bounds from the default bounds - stackBounds = pinnedStackController.getDefaultOrLastSavedBounds(); - } - - if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) { - return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio, - true /* useCurrentMinEdgeSize */); - } else { - return stackBounds; - } - } - } - - /** - * Animates the pinned stack. - */ - public void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, - int animationDuration, boolean fromFullscreen) { - synchronized (mGlobalLock) { - if (mContainer == null) { - throw new IllegalArgumentException("Pinned stack container not found :("); - } - - // Get the from-bounds - final Rect fromBounds = new Rect(); - mContainer.getBounds(fromBounds); - - // Get non-null fullscreen to-bounds for animating if the bounds are null - @SchedulePipModeChangedState int schedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - final boolean toFullscreen = toBounds == null; - if (toFullscreen) { - if (fromFullscreen) { - throw new IllegalArgumentException("Should not defer scheduling PiP mode" - + " change on animation to fullscreen."); - } - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; - - mService.getStackBounds( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); - if (!mTmpToBounds.isEmpty()) { - // If there is a fullscreen bounds, use that - toBounds = new Rect(mTmpToBounds); - } else { - // Otherwise, use the display bounds - toBounds = new Rect(); - mContainer.getDisplayContent().getBounds(toBounds); - } - } else if (fromFullscreen) { - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - mContainer.setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); - - final Rect finalToBounds = toBounds; - final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = - schedulePipModeChangedState; - final DisplayContent displayContent = mContainer.getDisplayContent(); - displayContent.mBoundsAnimationController.getHandler().post(() -> { - if (mContainer == null) { - return; - } - displayContent.mBoundsAnimationController.animateBounds(mContainer, fromBounds, - finalToBounds, animationDuration, finalSchedulePipModeChangedState, - fromFullscreen, toFullscreen); - }); - } - } - - /** - * Sets the current picture-in-picture aspect ratio. - */ - public void setPictureInPictureAspectRatio(float aspectRatio) { - synchronized (mGlobalLock) { - if (!mService.mSupportsPictureInPicture || mContainer == null) { - return; - } - - final PinnedStackController pinnedStackController = - mContainer.getDisplayContent().getPinnedStackController(); - - if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) { - mContainer.getAnimationOrCurrentBounds(mTmpFromBounds); - mTmpToBounds.set(mTmpFromBounds); - getPictureInPictureBounds(aspectRatio, mTmpToBounds); - if (!mTmpToBounds.equals(mTmpFromBounds)) { - animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */, - -1 /* duration */, false /* fromFullscreen */); - } - pinnedStackController.setAspectRatio( - pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) - ? aspectRatio : -1f); - } - } - } - - /** - * Sets the current picture-in-picture actions. - */ - public void setPictureInPictureActions(List<RemoteAction> actions) { - synchronized (mGlobalLock) { - if (!mService.mSupportsPictureInPicture || mContainer == null) { - return; - } - - mContainer.getDisplayContent().getPinnedStackController().setActions(actions); - } - } - - /** - * @return whether the multi-window mode change should be deferred as a part of a transition - * from fullscreen to non-fullscreen bounds. - */ - public boolean deferScheduleMultiWindowModeChanged() { - synchronized (mGlobalLock) { - return mContainer.deferScheduleMultiWindowModeChanged(); - } - } - - /** - * @return whether the stack can be resized from the bounds animation. - */ - public boolean pinnedStackResizeDisallowed() { - synchronized (mGlobalLock) { - return mContainer.pinnedStackResizeDisallowed(); - } - } - - /** - * The following calls are made from WM to AM. - */ - - /** Calls directly into activity manager so window manager lock shouldn't held. */ - public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, - boolean forceUpdate) { - if (mListener != null) { - PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener; - listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds, - forceUpdate); - } - } -} diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java deleted file mode 100644 index 33e8a60329bf..000000000000 --- a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2017 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.wm; - -import android.graphics.Rect; - -/** - * Interface used by the creator of {@link PinnedStackWindowController} to listen to changes with - * the stack container. - */ -public interface PinnedStackWindowListener extends StackWindowListener { - - /** - * Called when the stack container pinned stack animation will change the picture-in-picture - * mode. This is a direct call into ActivityManager. - */ - default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, - boolean forceUpdate) {} -} diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index f55c7c96e325..9b7214120aed 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -234,7 +234,7 @@ class RootActivityContainer extends ConfigurationContainer mWindowManager = wm; setWindowContainer(mWindowManager.mRoot); mDisplayManager = mService.mContext.getSystemService(DisplayManager.class); - mDisplayManager.registerDisplayListener(this, mService.mH); + mDisplayManager.registerDisplayListener(this, mService.mUiHandler); mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); final Display[] displays = mDisplayManager.getDisplays(); @@ -955,7 +955,7 @@ class RootActivityContainer extends ConfigurationContainer mWindowManager.deferSurfaceLayout(); final ActivityDisplay display = r.getActivityStack().getDisplay(); - PinnedActivityStack stack = display.getPinnedStack(); + ActivityStack stack = display.getPinnedStack(); // This will clear the pinned stack by moving an existing task to the full screen stack, // ensuring only one task is present. diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 937c9d9fc809..58cf73a9a2bd 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -47,10 +47,9 @@ import android.view.IWindowId; import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; -import android.view.Surface; +import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; -import android.view.InsetsState; import android.view.WindowManager; import com.android.internal.os.logging.MetricsLoggerWrapper; @@ -432,7 +431,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void insetsModified(IWindow window, InsetsState state) { - synchronized (mService.mWindowMap) { + synchronized (mService.mGlobalLock) { final WindowState windowState = mService.windowForClientLocked(this, window, false /* throwOnError */); if (windowState != null) { diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java deleted file mode 100644 index ada807b1ff3c..000000000000 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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 - */ - -package com.android.server.wm; - -import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; -import static com.android.server.wm.WindowContainer.POSITION_TOP; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.ref.WeakReference; - -/** - * Controller for the stack container. This is created by activity manager to link activity stacks - * to the stack container they use in window manager. - * - * Test class: {@link StackWindowControllerTests} - */ -public class StackWindowController - extends WindowContainerController<TaskStack, StackWindowListener> { - - private final int mStackId; - - private final H mHandler; - - final Rect mTmpBounds = new Rect(); - - public StackWindowController(int stackId, StackWindowListener listener, int displayId, - boolean onTop, Rect outBounds) { - this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance()); - } - - @VisibleForTesting - public StackWindowController(int stackId, StackWindowListener listener, - int displayId, boolean onTop, Rect outBounds, WindowManagerService service) { - super(listener, service); - mStackId = stackId; - mHandler = new H(new WeakReference<>(this), service.mH.getLooper()); - - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - throw new IllegalArgumentException("Trying to add stackId=" + stackId - + " to unknown displayId=" + displayId); - } - - dc.createStack(stackId, onTop, this); - getRawBounds(outBounds); - } - - @Override - public void removeContainer() { - if (mContainer != null) { - mContainer.removeIfPossible(); - super.removeContainer(); - } - } - - void reparent(int displayId, Rect outStackBounds, boolean onTop) { - if (mContainer == null) { - throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId - + " to displayId=" + displayId); - } - - final DisplayContent targetDc = mRoot.getDisplayContent(displayId); - if (targetDc == null) { - throw new IllegalArgumentException("Trying to move stackId=" + mStackId - + " to unknown displayId=" + displayId); - } - - targetDc.moveStackToDisplay(mContainer, onTop); - getRawBounds(outStackBounds); - } - - void positionChildAt(Task child, int position) { - if (DEBUG_STACK) { - Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position); - } - if (child == null) { - if (DEBUG_STACK) { - Slog.i(TAG_WM, "positionChildAt: could not find task=" + this); - } - return; - } - if (mContainer == null) { - if (DEBUG_STACK) { - Slog.i(TAG_WM, "positionChildAt: could not find stack for task=" + mContainer); - } - return; - } - child.positionAt(position); - mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); - } - - void positionChildAtTop(Task child, boolean includingParents) { - if (child == null) { - // TODO: Fix the call-points that cause this to happen. - return; - } - - mContainer.positionChildAt(POSITION_TOP, child, includingParents); - - final DisplayContent displayContent = mContainer.getDisplayContent(); - if (displayContent.mAppTransition.isTransitionSet()) { - child.setSendingToBottom(false); - } - displayContent.layoutAndAssignWindowLayersIfNeeded(); - } - - void positionChildAtBottom(Task child, boolean includingParents) { - if (child == null) { - // TODO: Fix the call-points that cause this to happen. - return; - } - - mContainer.positionChildAt(POSITION_BOTTOM, child, includingParents); - - if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) { - child.setSendingToBottom(true); - } - mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); - } - - /** - * Re-sizes a stack and its containing tasks. - * - * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen. - * @param taskBounds Bounds for tasks in the resized stack, keyed by task id. - * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id. - */ - public void resize(Rect bounds, SparseArray<Rect> taskBounds, - SparseArray<Rect> taskTempInsetBounds) { - if (mContainer == null) { - throw new IllegalArgumentException("resizeStack: stack " + this + " not found."); - } - // We might trigger a configuration change. Save the current task bounds for freezing. - mContainer.prepareFreezingTaskBounds(); - if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds) - && mContainer.isVisible()) { - mContainer.getDisplayContent().setLayoutNeeded(); - mService.mWindowPlacerLocked.performSurfacePlacement(); - } - } - - public void onPipAnimationEndResize() { - mContainer.onPipAnimationEndResize(); - } - - /** - * @see TaskStack.getStackDockedModeBoundsLocked(ConfigurationContainer, Rect, Rect, Rect) - */ - public void getStackDockedModeBounds(Configuration parentConfig, Rect dockedBounds, - Rect currentTempTaskBounds, - Rect outStackBounds, Rect outTempTaskBounds) { - if (mContainer != null) { - mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds, - currentTempTaskBounds, outStackBounds, outTempTaskBounds); - return; - } - outStackBounds.setEmpty(); - outTempTaskBounds.setEmpty(); - } - - public void prepareFreezingTaskBounds() { - if (mContainer == null) { - throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this - + " not found."); - } - mContainer.prepareFreezingTaskBounds(); - } - - public void getRawBounds(Rect outBounds) { - if (mContainer.matchParentBounds()) { - outBounds.setEmpty(); - } else { - mContainer.getRawBounds(outBounds); - } - } - - public void getBounds(Rect outBounds) { - if (mContainer != null) { - mContainer.getBounds(outBounds); - return; - } - outBounds.setEmpty(); - } - - void requestResize(Rect bounds) { - mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget(); - } - - @Override - public String toString() { - return "{StackWindowController stackId=" + mStackId + "}"; - } - - private static final class H extends Handler { - - static final int REQUEST_RESIZE = 0; - - private final WeakReference<StackWindowController> mController; - - H(WeakReference<StackWindowController> controller, Looper looper) { - super(looper); - mController = controller; - } - - @Override - public void handleMessage(Message msg) { - final StackWindowController controller = mController.get(); - final StackWindowListener listener = (controller != null) - ? controller.mListener : null; - if (listener == null) { - return; - } - switch (msg.what) { - case REQUEST_RESIZE: - listener.requestResize((Rect) msg.obj); - break; - } - } - } -} diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d334bd298ada..a7dd55b8a160 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -209,26 +209,14 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta super.removeImmediately(); } - void reparent(StackWindowController stackController, int position, boolean moveParents) { - if (DEBUG_STACK) { - Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId - + " to stack=" + stackController + " at " + position); - } - final TaskStack stack = stackController.mContainer; - if (stack == null) { - throw new IllegalArgumentException("reparent: could not find stack=" - + stackController); - } - reparent(stack, position, moveParents); - getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); - } - - void reparent(TaskStack stack, int position, boolean moveParents) { if (stack == mStack) { throw new IllegalArgumentException( "task=" + this + " already child of stack=" + mStack); } + if (stack == null) { + throw new IllegalArgumentException("reparent: could not find stack."); + } if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId + " from stack=" + mStack); EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask"); @@ -254,6 +242,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta onDisplayChanged(displayContent); prevDisplayContent.setLayoutNeeded(); } + getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); } /** @see ActivityTaskManagerService#positionTaskInStack(int, int, int). */ diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 6acd8641271c..f3050a903c70 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -709,7 +709,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { final List<TaskRecord> tasksToCheck = new ArrayList<>(); for (int i = 0; i < display.getChildCount(); ++i) { - ActivityStack<?> stack = display.getChildAt(i); + final ActivityStack stack = display.getChildAt(i); if (!stack.inFreeformWindowingMode()) { continue; } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 4a553cf59bc9..69dcaf473b12 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -455,17 +455,10 @@ class TaskRecord extends ConfigurationContainer { } final Rect bounds = updateOverrideConfigurationFromLaunchBounds(); - final StackWindowController stackController = getStack().getWindowContainerController(); + final TaskStack stack = getStack().getTaskStack(); - if (DEBUG_STACK) { - Slog.i(TAG_WM, "TaskRecord: taskId=" + taskId - + " stack=" + stackController + " bounds=" + bounds); - } - - final TaskStack stack = stackController.mContainer; if (stack == null) { - throw new IllegalArgumentException("TaskRecord: invalid stack=" - + stackController); + throw new IllegalArgumentException("TaskRecord: invalid stack=" + mStack); } EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId); mTask = new Task(taskId, stack, userId, mService.mWindowManager, mResizeMode, @@ -742,7 +735,7 @@ class TaskRecord extends ConfigurationContainer { // Must reparent first in window manager to avoid a situation where AM can delete the // we are coming from in WM before we reparent because it became empty. - mTask.reparent(toStack.getWindowContainerController(), position, + mTask.reparent(toStack.getTaskStack(), position, moveStackMode == REPARENT_MOVE_STACK_TO_FRONT); final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT @@ -1278,28 +1271,28 @@ class TaskRecord extends ConfigurationContainer { } /** - * Checks if the root activity requires a particular orientation (either by override or + * Checks if the top activity requires a particular orientation (either by override or * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED. */ - private int getRootActivityRequestedOrientation() { - ActivityRecord root = getRootActivity(); + private int getTopActivityRequestedOrientation() { + ActivityRecord top = getTopActivity(); if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED - || root == null) { + || top == null) { return getRequestedOverrideConfiguration().orientation; } - int rootScreenOrientation = root.getOrientation(); - if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + int screenOrientation = top.getOrientation(); + if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { // NOSENSOR means the display's "natural" orientation, so return that. ActivityDisplay display = mStack != null ? mStack.getDisplay() : null; if (display != null && display.mDisplayContent != null) { return mStack.getDisplay().mDisplayContent.getNaturalOrientation(); } - } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { + } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { // LOCKED means the activity's orientation remains unchanged, so return existing value. - return root.getConfiguration().orientation; - } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) { + return top.getConfiguration().orientation; + } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) { return ORIENTATION_LANDSCAPE; - } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) { + } else if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) { return ORIENTATION_PORTRAIT; } return ORIENTATION_UNDEFINED; @@ -2196,9 +2189,9 @@ class TaskRecord extends ConfigurationContainer { // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent" outOverrideBounds.setEmpty(); - // If the task or its root activity require a different orientation, make it fit the + // If the task or its top activity requires a different orientation, make it fit the // available bounds by scaling down its bounds. - int forcedOrientation = getRootActivityRequestedOrientation(); + int forcedOrientation = getTopActivityRequestedOrientation(); if (forcedOrientation != ORIENTATION_UNDEFINED && forcedOrientation != newParentConfig.orientation) { final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index ee74bdfaa113..8ed7d04f026d 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -21,6 +21,7 @@ import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LE import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; @@ -33,6 +34,10 @@ import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; +import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; +import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS; import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME; @@ -47,10 +52,12 @@ import static com.android.server.wm.StackProto.ID; import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT; import static com.android.server.wm.StackProto.TASKS; import static com.android.server.wm.StackProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.CallSuper; +import android.app.RemoteAction; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; @@ -71,9 +78,10 @@ import com.android.internal.policy.DockedDividerUtils; import com.android.server.EventLogTags; import java.io.PrintWriter; +import java.util.List; public class TaskStack extends WindowContainer<Task> implements - BoundsAnimationTarget { + BoundsAnimationTarget, ConfigurationContainerListener { /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to * restrict IME adjustment so that a min portion of top stack remains visible.*/ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; @@ -93,6 +101,10 @@ public class TaskStack extends WindowContainer<Task> implements private Rect mTmpRect2 = new Rect(); private Rect mTmpRect3 = new Rect(); + /** For Pinned stack controlling. */ + private Rect mTmpFromBounds = new Rect(); + private Rect mTmpToBounds = new Rect(); + /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ private final Rect mAdjustedBounds = new Rect(); @@ -141,6 +153,9 @@ public class TaskStack extends WindowContainer<Task> implements private Dimmer mDimmer = new Dimmer(this); + // TODO: remove after unification. + ActivityStack mActivityStack; + /** * For {@link #prepareSurfaces}. */ @@ -150,10 +165,11 @@ public class TaskStack extends WindowContainer<Task> implements private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry = new AnimatingAppWindowTokenRegistry(); - TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { + TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack) { super(service); mStackId = stackId; - setController(controller); + mActivityStack = activityStack; + activityStack.registerConfigurationChangeListener(this); mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_minimize_thickness); EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); @@ -572,6 +588,49 @@ public class TaskStack extends WindowContainer<Task> implements positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers); } + void positionChildAt(Task child, int position) { + if (DEBUG_STACK) { + Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position); + } + if (child == null) { + if (DEBUG_STACK) { + Slog.i(TAG_WM, "positionChildAt: could not find task=" + this); + } + return; + } + child.positionAt(position); + getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + } + + void positionChildAtTop(Task child, boolean includingParents) { + if (child == null) { + // TODO: Fix the call-points that cause this to happen. + return; + } + + positionChildAt(POSITION_TOP, child, includingParents); + + final DisplayContent displayContent = getDisplayContent(); + if (displayContent.mAppTransition.isTransitionSet()) { + child.setSendingToBottom(false); + } + displayContent.layoutAndAssignWindowLayersIfNeeded(); + } + + void positionChildAtBottom(Task child, boolean includingParents) { + if (child == null) { + // TODO: Fix the call-points that cause this to happen. + return; + } + + positionChildAt(POSITION_BOTTOM, child, includingParents); + + if (getDisplayContent().mAppTransition.isTransitionSet()) { + child.setSendingToBottom(true); + } + getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + } + @Override void positionChildAt(int position, Task child, boolean includingParents) { positionChildAt(position, child, includingParents, child.showForAllUsers()); @@ -596,6 +655,21 @@ public class TaskStack extends WindowContainer<Task> implements EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition); } + void reparent(int displayId, Rect outStackBounds, boolean onTop) { + final DisplayContent targetDc = mWmService.mRoot.getDisplayContent(displayId); + if (targetDc == null) { + throw new IllegalArgumentException("Trying to move stackId=" + mStackId + + " to unknown displayId=" + displayId); + } + + targetDc.moveStackToDisplay(this, onTop); + if (matchParentBounds()) { + outStackBounds.setEmpty(); + } else { + getRawBounds(outStackBounds); + } + } + // TODO: We should really have users as a window container in the hierarchy so that we don't // have to do complicated things like we are doing in this method. private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, @@ -725,6 +799,23 @@ public class TaskStack extends WindowContainer<Task> implements } /** + * Re-sizes a stack and its containing tasks. + * + * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen. + * @param taskBounds Bounds for tasks in the resized stack, keyed by task id. + * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id. + */ + void resize(Rect bounds, SparseArray<Rect> taskBounds, + SparseArray<Rect> taskTempInsetBounds) { + // We might trigger a configuration change. Save the current task bounds for freezing. + prepareFreezingTaskBounds(); + if (setBounds(bounds, taskBounds, taskTempInsetBounds) && isVisible()) { + getDisplayContent().setLayoutNeeded(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); + } + } + + /** * Calculate an amount by which to expand the stack bounds in each direction. * Used to make room for shadows in the pinned windowing mode. */ @@ -929,12 +1020,7 @@ public class TaskStack extends WindowContainer<Task> implements (dockedStack == null || dockedStack == this) ? null : dockedStack.getRawBounds(); getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), dockedBounds, null /* currentTempTaskBounds */, bounds, tempBounds); - getController().requestResize(bounds); - } - - @Override - StackWindowController getController() { - return (StackWindowController) super.getController(); + mActivityStack.requestResize(bounds); } @Override @@ -947,6 +1033,14 @@ public class TaskStack extends WindowContainer<Task> implements } @Override + void removeImmediately() { + if (mActivityStack != null) { + mActivityStack.unregisterConfigurationChangeListener(this); + } + super.removeImmediately(); + } + + @Override void onParentSet() { super.onParentSet(); @@ -1572,14 +1666,13 @@ public class TaskStack extends WindowContainer<Task> implements // I don't believe you... } - final PinnedStackWindowController controller = - (PinnedStackWindowController) getController(); - if (schedulePipModeChangedCallback && controller != null) { + if (schedulePipModeChangedCallback && mActivityStack != null) { // We need to schedule the PiP mode change before the animation up. It is possible // in this case for the animation down to not have been completed, so always // force-schedule and update to the client to ensure that it is notified that it // is no longer in picture-in-picture mode - controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate); + mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(null, + forceUpdate); } } return true; @@ -1592,12 +1685,10 @@ public class TaskStack extends WindowContainer<Task> implements // Update to the final bounds if requested. This is done here instead of in the bounds // animator to allow us to coordinate this after we notify the PiP mode changed - final PinnedStackWindowController controller = - (PinnedStackWindowController) getController(); - if (schedulePipModeChangedCallback && controller != null) { + if (schedulePipModeChangedCallback) { // We need to schedule the PiP mode change after the animation down, so use the // final bounds - controller.updatePictureInPictureModeForPinnedStackAnimation( + mActivityStack.updatePictureInPictureModeForPinnedStackAnimation( mBoundsAnimationTarget, false /* forceUpdate */); } @@ -1624,6 +1715,135 @@ public class TaskStack extends WindowContainer<Task> implements } } + /** + * @return the current stack bounds transformed to the given {@param aspectRatio}. If + * the default bounds is {@code null}, then the {@param aspectRatio} is applied to the + * default bounds. + */ + Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) { + if (!mWmService.mSupportsPictureInPicture) { + return null; + } + + final DisplayContent displayContent = getDisplayContent(); + if (displayContent == null) { + return null; + } + + if (!inPinnedWindowingMode()) { + return null; + } + + final PinnedStackController pinnedStackController = + displayContent.getPinnedStackController(); + if (stackBounds == null) { + // Calculate the aspect ratio bounds from the default bounds + stackBounds = pinnedStackController.getDefaultOrLastSavedBounds(); + } + + if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) { + return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio, + true /* useCurrentMinEdgeSize */); + } else { + return stackBounds; + } + } + + /** + * Animates the pinned stack. + */ + void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, + int animationDuration, boolean fromFullscreen) { + if (!inPinnedWindowingMode()) { + return; + } + // Get the from-bounds + final Rect fromBounds = new Rect(); + getBounds(fromBounds); + + // Get non-null fullscreen to-bounds for animating if the bounds are null + @SchedulePipModeChangedState int schedulePipModeChangedState = + NO_PIP_MODE_CHANGED_CALLBACKS; + final boolean toFullscreen = toBounds == null; + if (toFullscreen) { + if (fromFullscreen) { + throw new IllegalArgumentException("Should not defer scheduling PiP mode" + + " change on animation to fullscreen."); + } + schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; + + mWmService.getStackBounds( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); + if (!mTmpToBounds.isEmpty()) { + // If there is a fullscreen bounds, use that + toBounds = new Rect(mTmpToBounds); + } else { + // Otherwise, use the display bounds + toBounds = new Rect(); + getDisplayContent().getBounds(toBounds); + } + } else if (fromFullscreen) { + schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; + } + + setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); + + final Rect finalToBounds = toBounds; + final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = + schedulePipModeChangedState; + final DisplayContent displayContent = getDisplayContent(); + displayContent.mBoundsAnimationController.getHandler().post(() -> { + displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, + finalToBounds, animationDuration, finalSchedulePipModeChangedState, + fromFullscreen, toFullscreen); + }); + } + + /** + * Sets the current picture-in-picture aspect ratio. + */ + void setPictureInPictureAspectRatio(float aspectRatio) { + if (!mWmService.mSupportsPictureInPicture) { + return; + } + + if (!inPinnedWindowingMode()) { + return; + } + + final PinnedStackController pinnedStackController = + getDisplayContent().getPinnedStackController(); + + if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) { + return; + } + getAnimationOrCurrentBounds(mTmpFromBounds); + mTmpToBounds.set(mTmpFromBounds); + getPictureInPictureBounds(aspectRatio, mTmpToBounds); + if (!mTmpToBounds.equals(mTmpFromBounds)) { + animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */, + -1 /* duration */, false /* fromFullscreen */); + } + pinnedStackController.setAspectRatio( + pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) + ? aspectRatio : -1f); + } + + /** + * Sets the current picture-in-picture actions. + */ + void setPictureInPictureActions(List<RemoteAction> actions) { + if (!mWmService.mSupportsPictureInPicture) { + return; + } + + if (!inPinnedWindowingMode()) { + return; + } + + getDisplayContent().getPinnedStackController().setActions(actions); + } + @Override public boolean isAttached() { synchronized (mWmService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index b2194190f4f4..905787051de8 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -67,7 +67,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { return; } WindowContainer parent = mDisplayContent.getParent(); - if (parent != null) { + if (parent != null && parent.getTopChild() != mDisplayContent) { parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent, true /* includingParents */); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 25e61f876722..19058776d187 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -437,6 +437,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mChildren.peekLast() != child) { mChildren.remove(child); mChildren.add(child); + onChildPositionChanged(); } if (includingParents && getParent() != null) { getParent().positionChildAt(POSITION_TOP, this /* child */, @@ -447,6 +448,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mChildren.peekFirst() != child) { mChildren.remove(child); mChildren.addFirst(child); + onChildPositionChanged(); } if (includingParents && getParent() != null) { getParent().positionChildAt(POSITION_BOTTOM, this /* child */, @@ -460,8 +462,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // doing this adjustment here and remove any adjustments in the callers. mChildren.remove(child); mChildren.add(position, child); + onChildPositionChanged(); } - onChildPositionChanged(); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 90506e744250..fda7a85c1270 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -723,7 +723,7 @@ public class WindowManagerService extends IWindowManager.Stub void updateSystemUiSettings() { boolean changed; - synchronized (mWindowMap) { + synchronized (mGlobalLock) { changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext) || PolicyControl.reloadFromSetting(mContext); } @@ -2629,12 +2629,23 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void onUserSwitched() { mSettingsObserver.updateSystemUiSettings(); - synchronized (mWindowMap) { + synchronized (mGlobalLock) { // force a re-application of focused window sysui visibility on each display. mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw); } } + @Override + public void moveDisplayToTop(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && mRoot.getTopChild() != displayContent) { + mRoot.positionChildAt(WindowContainer.POSITION_TOP, displayContent, + true /* includingParents */); + } + } + } + /** * Starts deferring layout passes. Useful when doing multiple changes but to optimize * performance, only one layout pass should be done. This can be called multiple times, and @@ -5932,8 +5943,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" apps="); pw.print(mAppsFreezingScreen); final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); pw.print(" mRotation="); pw.print(defaultDisplayContent.getRotation()); - pw.print(" mAltOrientation="); - pw.println(defaultDisplayContent.getAltOrientation()); pw.print(" mLastWindowForcedOrientation="); pw.print(defaultDisplayContent.getLastWindowForcedOrientation()); pw.print(" mLastOrientation="); @@ -6379,7 +6388,7 @@ public class WindowManagerService extends IWindowManager.Stub } void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) { - synchronized (mWindowMap) { + synchronized (mGlobalLock) { mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays; } } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index c8c5e8f136af..fb00aebb622f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -107,6 +107,7 @@ cc_defaults { "android.hardware.gnss@1.0", "android.hardware.gnss@1.1", "android.hardware.gnss@2.0", + "android.hardware.gnss.visibility_control@1.0", "android.hardware.input.classifier@1.0", "android.hardware.ir@1.0", "android.hardware.light@2.0", diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index b290bc516320..dcb2ff5f9df5 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -26,6 +26,7 @@ #include <android/hardware/gnss/1.1/IGnssMeasurement.h> #include <android/hardware/gnss/2.0/IGnssMeasurement.h> #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h> +#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h> #include <nativehelper/JNIHelp.h> #include "jni.h" #include "hardware_legacy/power.h" @@ -88,6 +89,8 @@ static jmethodID method_correctionPlaneLatDeg; static jmethodID method_correctionPlaneLngDeg; static jmethodID method_correctionPlaneAltDeg; static jmethodID method_correctionPlaneAzimDeg; +static jmethodID method_reportNfwNotification; +static jmethodID method_isInEmergencySession; /* * Save a pointer to JavaVm to attach/detach threads executing @@ -152,6 +155,9 @@ using IAGnssCallback_V2_0 = android::hardware::gnss::V2_0::IAGnssCallback; using IMeasurementCorrections = android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections; +using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl; +using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback; + struct GnssDeathRecipient : virtual public hidl_death_recipient { // hidl_death_recipient interface @@ -190,7 +196,7 @@ sp<IMeasurementCorrections> gnssCorrectionsIface = nullptr; // This boolean is needed to ensure that Gnsss Measurement Corrections related method are only // initalized when needed which will be few devices initially bool firstGnssMeasurementCorrectionInjected = false; - +sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr; #define WAKE_LOCK_NAME "GPS" @@ -856,7 +862,7 @@ struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 { Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0( const IGnssMeasurementCallback_V2_0::GnssData& data) { - // TODO(b/119571122): implement gnssMeasurementCb_2_0 + translateAndSetGnssData(data); return Void(); } @@ -894,9 +900,8 @@ size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_ return data.measurementCount; } -template<> -size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData> - (const IGnssMeasurementCallback_V1_1::GnssData& data) { +template<class T> +size_t GnssMeasurementCallback::getMeasurementCount(const T& data) { return data.measurements.size(); } @@ -958,6 +963,17 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement ADR_STATE_HALF_CYCLE_REPORTED)); } +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template<> +void GnssMeasurementCallback::translateSingleGnssMeasurement + <IGnssMeasurementCallback_V2_0::GnssMeasurement>( + const IGnssMeasurementCallback_V2_0::GnssMeasurement* measurement_V2_0, + JavaObject& object) { + translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object); + + SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType))); +} + jobject GnssMeasurementCallback::translateGnssClock( JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) { JavaObject object(env, "android/location/GnssClock"); @@ -1080,6 +1096,54 @@ Return<void> GnssNiCallback::niNotifyCb( } /* + * GnssVisibilityControlCallback implements callback methods of IGnssVisibilityControlCallback.hal. + */ +struct GnssVisibilityControlCallback : public IGnssVisibilityControlCallback { + Return<void> nfwNotifyCb(const IGnssVisibilityControlCallback::NfwNotification& notification) + override; + Return<bool> isInEmergencySession() override; +}; + +Return<void> GnssVisibilityControlCallback::nfwNotifyCb( + const IGnssVisibilityControlCallback::NfwNotification& notification) { + JNIEnv* env = getJniEnv(); + jstring proxyAppPackageName = env->NewStringUTF(notification.proxyAppPackageName.c_str()); + jstring otherProtocolStackName = env->NewStringUTF(notification.otherProtocolStackName.c_str()); + jstring requestorId = env->NewStringUTF(notification.requestorId.c_str()); + + if (proxyAppPackageName && otherProtocolStackName && requestorId) { + env->CallVoidMethod(mCallbacksObj, method_reportNfwNotification, proxyAppPackageName, + notification.protocolStack, otherProtocolStackName, + notification.requestor, requestorId, + notification.inEmergencyMode, notification.isCachedLocation); + } else { + ALOGE("%s: OOM Error\n", __func__); + } + + if (requestorId) { + env->DeleteLocalRef(requestorId); + } + + if (otherProtocolStackName) { + env->DeleteLocalRef(otherProtocolStackName); + } + + if (proxyAppPackageName) { + env->DeleteLocalRef(proxyAppPackageName); + } + + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +Return<bool> GnssVisibilityControlCallback::isInEmergencySession() { + JNIEnv* env = getJniEnv(); + auto result = env->CallBooleanMethod(mCallbacksObj, method_isInEmergencySession); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return result; +} + +/* * AGnssCallback_V1_0 implements callback methods required by the IAGnssCallback 1.0 interface. */ struct AGnssCallback_V1_0 : public IAGnssCallback_V1_0 { @@ -1313,6 +1377,9 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass "reportLocationBatch", "([Landroid/location/Location;)V"); method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V"); + method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification", + "(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V"); + method_isInEmergencySession = env->GetMethodID(clazz, "isInEmergencySession", "()Z"); /* * Save a pointer to JVM. @@ -1388,12 +1455,6 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass if (gnssHal_V2_0 != nullptr) { // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0 auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0(); - auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections(); - if (!gnssCorrections.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurementCorrections interface"); - } else { - gnssCorrectionsIface = gnssCorrections; - } if (!gnssMeasurement.isOk()) { ALOGD("Unable to get a handle to GnssMeasurement_V2_0"); } else { @@ -1401,6 +1462,12 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0; gnssMeasurementIface = gnssMeasurementIface_V2_0; } + auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections(); + if (!gnssCorrections.isOk()) { + ALOGD("Unable to get a handle to GnssMeasurementCorrections interface"); + } else { + gnssCorrectionsIface = gnssCorrections; + } } else if (gnssHal_V1_1 != nullptr) { auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); if (!gnssMeasurement.isOk()) { @@ -1471,6 +1538,15 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass } else { gnssBatchingIface = gnssBatching; } + + if (gnssHal_V2_0 != nullptr) { + auto gnssVisibilityControl = gnssHal_V2_0->getExtensionVisibilityControl(); + if (!gnssVisibilityControl.isOk()) { + ALOGD("Unable to get a handle to GnssVisibilityControl interface"); + } else { + gnssVisibilityControlIface = gnssVisibilityControl; + } + } } static jboolean android_location_GnssLocationProvider_is_supported( @@ -1572,7 +1648,13 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject if (agnssRilIface != nullptr) { agnssRilIface->setCallback(aGnssRilCbIface); } else { - ALOGI("Unable to Initialize AGnss Ril interface\n"); + ALOGI("Unable to initialize AGnss Ril interface\n"); + } + + if (gnssVisibilityControlIface != nullptr) { + sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface = + new GnssVisibilityControlCallback(); + gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface); } return JNI_TRUE; @@ -1974,6 +2056,11 @@ static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv* return result; } +static jboolean android_location_GnssLocationProvider_is_gnss_visibility_control_supported( + JNIEnv* /* env */, jclass /* clazz */) { + return (gnssVisibilityControlIface != nullptr) ? JNI_TRUE : JNI_FALSE; +} + static void android_location_GnssNetworkConnectivityHandler_update_network_state(JNIEnv* env, jobject /* obj */, jboolean connected, @@ -2557,6 +2644,29 @@ static jboolean android_location_GnssBatchingProvider_stop_batch(JNIEnv*, jclass return gnssBatchingIface->stop(); } +static jboolean android_location_GnssVisibilityControl_enable_nfw_location_access( + JNIEnv* env, jobject, jobjectArray proxyApps) { + if (gnssVisibilityControlIface == nullptr) { + ALOGI("No GNSS Visibility Control interface available"); + return JNI_FALSE; + } + + const jsize length = env->GetArrayLength(proxyApps); + hidl_vec<hidl_string> hidlProxyApps(length); + for (int i = 0; i < length; ++i) { + jstring proxyApp = (jstring) (env->GetObjectArrayElement(proxyApps, i)); + ScopedJniString jniProxyApp(env, proxyApp); + hidlProxyApps[i] = jniProxyApp; + } + + auto result = gnssVisibilityControlIface->enableNfwLocationAccess(hidlProxyApps); + if (result.isOk()) { + return result; + } else { + return JNI_FALSE; + } +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", reinterpret_cast<void *>( @@ -2607,6 +2717,8 @@ static const JNINativeMethod sMethods[] = { {"native_get_internal_state", "()Ljava/lang/String;", reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)}, + {"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void *>( + android_location_GnssLocationProvider_is_gnss_visibility_control_supported)}, }; static const JNINativeMethod sMethodsBatching[] = { @@ -2736,6 +2848,14 @@ static const JNINativeMethod sConfigurationMethods[] = { reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)}, }; +static const JNINativeMethod sVisibilityControlMethods[] = { + /* name, signature, funcPtr */ + {"native_enable_nfw_location_access", + "([Ljava/lang/String;)Z", + reinterpret_cast<void *>( + android_location_GnssVisibilityControl_enable_nfw_location_access)}, +}; + int register_android_server_location_GnssLocationProvider(JNIEnv* env) { jniRegisterNativeMethods( env, @@ -2767,6 +2887,11 @@ int register_android_server_location_GnssLocationProvider(JNIEnv* env) { "com/android/server/location/GnssConfiguration", sConfigurationMethods, NELEM(sConfigurationMethods)); + jniRegisterNativeMethods( + env, + "com/android/server/location/GnssVisibilityControl", + sVisibilityControlMethods, + NELEM(sVisibilityControlMethods)); return jniRegisterNativeMethods( env, "com/android/server/location/GnssLocationProvider", diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java new file mode 100644 index 000000000000..eaab6507e8d4 --- /dev/null +++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java @@ -0,0 +1,143 @@ +/* + * 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.net.ipmemorystore; + +import android.annotation.NonNull; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +/** + * Encapsulating class for using the SQLite database backing the memory store. + * + * This class groups together the contracts and the SQLite helper used to + * use the database. + * + * @hide + */ +public class IpMemoryStoreDatabase { + /** + * Contract class for the Network Attributes table. + */ + public static class NetworkAttributesContract { + public static final String TABLENAME = "NetworkAttributes"; + + public static final String COLNAME_L2KEY = "l2Key"; + public static final String COLTYPE_L2KEY = "TEXT NOT NULL"; + + public static final String COLNAME_EXPIRYDATE = "expiryDate"; + // Milliseconds since the Epoch, in true Java style + public static final String COLTYPE_EXPIRYDATE = "BIGINT"; + + public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address"; + public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER"; + + // Please note that the group hint is only a *hint*, hence its name. The client can offer + // this information to nudge the grouping in the decision it thinks is right, but it can't + // decide for the memory store what is the same L3 network. + public static final String COLNAME_GROUPHINT = "groupHint"; + public static final String COLTYPE_GROUPHINT = "TEXT"; + + public static final String COLNAME_DNSADDRESSES = "dnsAddresses"; + // Stored in marshalled form as is + public static final String COLTYPE_DNSADDRESSES = "BLOB"; + + public static final String COLNAME_MTU = "mtu"; + public static final String COLTYPE_MTU = "INTEGER"; + + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + + TABLENAME + " (" + + COLNAME_L2KEY + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, " + + COLNAME_EXPIRYDATE + " " + COLTYPE_EXPIRYDATE + ", " + + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", " + + COLNAME_GROUPHINT + " " + COLTYPE_GROUPHINT + ", " + + COLNAME_DNSADDRESSES + " " + COLTYPE_DNSADDRESSES + ", " + + COLNAME_MTU + " " + COLTYPE_MTU + ")"; + public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME; + } + + /** + * Contract class for the Private Data table. + */ + public static class PrivateDataContract { + public static final String TABLENAME = "PrivateData"; + + public static final String COLNAME_L2KEY = "l2Key"; + public static final String COLTYPE_L2KEY = "TEXT NOT NULL"; + + public static final String COLNAME_CLIENT = "client"; + public static final String COLTYPE_CLIENT = "TEXT NOT NULL"; + + public static final String COLNAME_DATANAME = "dataName"; + public static final String COLTYPE_DATANAME = "TEXT NOT NULL"; + + public static final String COLNAME_DATA = "data"; + public static final String COLTYPE_DATA = "BLOB NOT NULL"; + + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + + TABLENAME + " (" + + COLNAME_L2KEY + " " + COLTYPE_L2KEY + ", " + + COLNAME_CLIENT + " " + COLTYPE_CLIENT + ", " + + COLNAME_DATANAME + " " + COLTYPE_DATANAME + ", " + + COLNAME_DATA + " " + COLTYPE_DATA + ", " + + "PRIMARY KEY (" + + COLNAME_L2KEY + ", " + + COLNAME_CLIENT + ", " + + COLNAME_DATANAME + "))"; + public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME; + } + + // To save memory when the DB is not used, close it after 30s of inactivity. This is + // determined manually based on what feels right. + private static final long IDLE_CONNECTION_TIMEOUT_MS = 30_000; + + /** The SQLite DB helper */ + public static class DbHelper extends SQLiteOpenHelper { + // Update this whenever changing the schema. + private static final int SCHEMA_VERSION = 1; + private static final String DATABASE_FILENAME = "IpMemoryStore.db"; + + public DbHelper(@NonNull final Context context) { + super(context, DATABASE_FILENAME, null, SCHEMA_VERSION); + setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS); + } + + /** Called when the database is created */ + public void onCreate(@NonNull final SQLiteDatabase db) { + db.execSQL(NetworkAttributesContract.CREATE_TABLE); + db.execSQL(PrivateDataContract.CREATE_TABLE); + } + + /** Called when the database is upgraded */ + public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, + final int newVersion) { + // No upgrade supported yet. + db.execSQL(NetworkAttributesContract.DROP_TABLE); + db.execSQL(PrivateDataContract.DROP_TABLE); + onCreate(db); + } + + /** Called when the database is downgraded */ + public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion, + final int newVersion) { + // Downgrades always nuke all data and recreate an empty table. + db.execSQL(NetworkAttributesContract.DROP_TABLE); + db.execSQL(PrivateDataContract.DROP_TABLE); + onCreate(db); + } + } +} diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java index c9759bf6170f..55a72190eff4 100644 --- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java +++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java @@ -19,6 +19,8 @@ package com.android.server.net.ipmemorystore; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; import android.net.IIpMemoryStore; import android.net.ipmemorystore.Blob; import android.net.ipmemorystore.IOnBlobRetrievedListener; @@ -27,6 +29,10 @@ import android.net.ipmemorystore.IOnNetworkAttributesRetrieved; import android.net.ipmemorystore.IOnSameNetworkResponseListener; import android.net.ipmemorystore.IOnStatusListener; import android.net.ipmemorystore.NetworkAttributesParcelable; +import android.util.Log; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * Implementation for the IP memory store. @@ -37,10 +43,75 @@ import android.net.ipmemorystore.NetworkAttributesParcelable; * @hide */ public class IpMemoryStoreService extends IIpMemoryStore.Stub { + private static final String TAG = IpMemoryStoreService.class.getSimpleName(); + private static final int MAX_CONCURRENT_THREADS = 4; + + @NonNull final Context mContext; + @Nullable + final SQLiteDatabase mDb; + @NonNull + final ExecutorService mExecutor; + /** + * Construct an IpMemoryStoreService object. + * This constructor will block on disk access to open the database. + * @param context the context to access storage with. + */ public IpMemoryStoreService(@NonNull final Context context) { + // Note that constructing the service will access the disk and block + // for some time, but it should make no difference to the clients. Because + // the interface is one-way, clients fire and forget requests, and the callback + // will get called eventually in any case, and the framework will wait for the + // service to be created to deliver subsequent requests. + // Avoiding this would mean the mDb member can't be final, which means the service would + // have to test for nullity, care for failure, and allow for a wait at every single access, + // which would make the code a lot more complex and require all methods to possibly block. mContext = context; + SQLiteDatabase db; + final IpMemoryStoreDatabase.DbHelper helper = new IpMemoryStoreDatabase.DbHelper(context); + try { + db = helper.getWritableDatabase(); + if (null == db) Log.e(TAG, "Unexpected null return of getWriteableDatabase"); + } catch (final SQLException e) { + Log.e(TAG, "Can't open the Ip Memory Store database", e); + db = null; + } catch (final Exception e) { + Log.wtf(TAG, "Impossible exception Ip Memory Store database", e); + db = null; + } + mDb = db; + // The work-stealing thread pool executor will spawn threads as needed up to + // the max only when there is no free thread available. This generally behaves + // exactly like one would expect it intuitively : + // - When work arrives, it will spawn a new thread iff there are no available threads + // - When there is no work to do it will shutdown threads after a while (the while + // being equal to 2 seconds (not configurable) when max threads are spun up and + // twice as much for every one less thread) + // - When all threads are busy the work is enqueued and waits for any worker + // to become available. + // Because the stealing pool is made for very heavily parallel execution of + // small tasks that spawn others, it creates a queue per thread that in this + // case is overhead. However, the three behaviors above make it a superior + // choice to cached or fixedThreadPoolExecutor, neither of which can actually + // enqueue a task waiting for a thread to be free. This can probably be solved + // with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous + // complexity for little benefit in this case. + mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS); + } + + /** + * Shutdown the memory store service, cancelling running tasks and dropping queued tasks. + * + * This is provided to give a way to clean up, and is meant to be available in case of an + * emergency shutdown. + */ + public void shutdown() { + // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries + // to cancel the existing tasks, and does not wait for completion. It does not + // guarantee the threads can be terminated in any given amount of time. + mExecutor.shutdownNow(); + if (mDb != null) mDb.close(); } /** @@ -61,7 +132,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { public void storeNetworkAttributes(@NonNull final String l2Key, @NonNull final NetworkAttributesParcelable attributes, @Nullable final IOnStatusListener listener) { - // TODO : implement this + // TODO : implement this. } /** @@ -79,7 +150,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final Blob data, @Nullable final IOnStatusListener listener) { - // TODO : implement this + // TODO : implement this. } /** @@ -99,7 +170,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { @Override public void findL2Key(@NonNull final NetworkAttributesParcelable attributes, @NonNull final IOnL2KeyResponseListener listener) { - // TODO : implement this + // TODO : implement this. } /** @@ -114,7 +185,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { @Override public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2, @NonNull final IOnSameNetworkResponseListener listener) { - // TODO : implement this + // TODO : implement this. } /** diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java new file mode 100644 index 000000000000..aa454008958d --- /dev/null +++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java @@ -0,0 +1,307 @@ +/* + * 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.net.ipmemorystore; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * A class containing the logic around the relevance value for + * IP Memory Store. + * + * @hide + */ +public class RelevanceUtils { + /** + * The relevance is a decaying value that gets lower and lower until it + * reaches 0 after some time passes. It follows an exponential decay law, + * dropping slowly at first then faster and faster, because a network is + * likely to be visited again if it was visited not long ago, and the longer + * it hasn't been visited the more likely it is that it won't be visited + * again. For example, a network visited on holiday should stay fresh for + * the duration of the holiday and persist for a while, but after the venue + * hasn't been visited for a while it should quickly be discarded. What + * should accelerate forgetting the network is extended periods without + * visits, so that occasional venues get discarded but regular visits keep + * the network relevant, even if the visits are infrequent. + * + * This function must be stable by iteration, meaning that adjusting the same value + * for different dates iteratively multiple times should give the same result. + * Formally, if f is the decay function that associates a relevance x at a date d1 + * to the value at ulterior date d3, then for any date d2 between d1 and d3 : + * f(x, d3 - d1) = f(f(x, d3 - d2), d2 - d1). Intuitively, this property simply + * means it should be the same to compute and store back the value after two months, + * or to do it once after one month, store it back, and do it again after another + * months has passed. + * The pair of the relevance and date define the entire curve, so any pair + * of values on the curve will define the same curve. Setting one of them to a + * constant, so as not to have to store it, means the other one will always suffice + * to describe the curve. For example, only storing the date for a known, constant + * value of the relevance is an efficient way of remembering this information (and + * to compare relevances together, as f is monotonically decreasing). + * + *** Choosing the function : + * Functions of the kind described above are standard exponential decay functions + * like the ones that govern atomic decay where the value at any given date can be + * computed uniformly from the value at a previous date and the time elapsed since + * that date. It is simple to picture this kind of function as one where after a + * given period of time called the half-life, the relevance value will have been + * halved. Decay of this kind is expressed in function of the previous value by + * functions like + * f(x, t) = x * F ^ (t / L) + * ...where x is the value, t is the elapsed time, L is the half-life (or more + * generally the F-th-life) and F the decay factor (typically 0.5, hence why L is + * usually called the half-life). The ^ symbol here is used for exponentiation. + * Or, starting at a given M for t = 0 : + * f(t) = M * F ^ (t / L) + * + * Because a line in the store needs to become irrelevant at some point but + * this class of functions never go to 0, a minimum cutoff has to be chosen to + * represent irrelevance. The simpler way of doing this is to simply add this + * minimum cutoff to the computation before and removing it after. + * Thus the function becomes : + * f(x, t) = ((x + K) * F ^ (t / L)) - K + * ...where K is the minimum cutoff, L the half-life, and F the factor between + * the original x and x after its half-life. Strictly speaking using the word + * "half-life" implies that F = 0.5, but the relation works for any value of F. + * + * It is easy enough to check that this function satisfies the stability + * relation that was given above for any value of F, L and K, which become + * parameters that can be defined at will. + * + * relevance + * 1.0 | + * |\ + * | \ + * | \ (this graph rendered with L = 75 days and K = 1/40) + * 0.75| ', + * | \ + * | '. + * | \. + * | \ + * 0.5 | '\ + * | ''. + * | ''. + * | ''. + * 0.25| '''.. + * | '''.. + * | ''''.... + * | '''''.......... + * 0 +-------------------------------------------------------''''''''''---- + * 0 50 100 150 200 250 300 350 400 days + * + *** Choosing the parameters + * The maximum M is an arbitrary parameter that simply scales the curve. + * The tradeoff for M is pretty simple : if the relevance is going to be an + * integer, the bigger M is the more precision there is in the relevance. + * However, values of M that are easy for humans to read are preferable to + * help debugging, and a suitably low value may be enough to ensure there + * won't be integer overflows in intermediate computations. + * A value of 1_000_000 probably is plenty for precision, while still in the + * low range of what ints can represent. + * + * F and L are parameters to be chosen arbitrarily and have an impact on how + * fast the relevance will be decaying at first, keeping in mind that + * the 400 days value and the cap stay the same. In simpler words, F and L + * define the steepness of the curve. + * To keep things simple (and familiar) F is arbitrarily chosen to be 0.5, and + * L is set to 200 days visually to achieve the desired effect. Refer to the + * illustration above to get a feel of how that feels. + * + * Moreover, the memory store works on an assumption that the relevance should + * be capped, and that an entry with capped relevance should decay in 400 days. + * This is on premises that the networks a device will need to remember the + * longest should be networks visited about once a year. + * For this reason, the relevance is at the maximum M 400 days before expiry : + * f(M, 400 days) = 0 + * From replacing this with the value of the function, K can then be derived + * from the values of M, F and L : + * (M + K) * F ^ (t / L) - K = 0 + * K = M * F ^ (400 days / L) / (1 - F ^ (400 days / L)) + * Replacing with actual values this gives : + * K = 1_000_000 * 0.5 ^ (400 / 200) / (1 - 0.5 ^ (400 / 200)) + * = 1_000_000 / 3 ≈ 333_333.3 + * This ensures the function has the desired profile, the desired value at + * cap, and the desired value at expiry. + * + *** Useful relations + * Let's define the expiry time for any given relevance x as the interval of + * time such as : + * f(x, expiry) = 0 + * which can be rewritten + * ((x + K) * F ^ (expiry / L)) = K + * ...giving an expression of the expiry in function of the relevance x as + * expiry = L * logF(K / (x + K)) + * Conversely the relevance x can be expressed in function of the expiry as + * x = K / F ^ (expiry / L) - K + * These relations are useful in utility functions. + * + *** Bumping things up + * The last issue therefore is to decide how to bump up the relevance. The + * simple approach is to simply lift up the curve a little bit by a constant + * normalized amount, delaying the time of expiry. For example increasing + * the relevance by an amount I gives : + * x2 = x1 + I + * x2 and x1 correspond to two different expiry times expiry2 and expiry1, + * and replacing x1 and x2 in the relation above with their expression in + * function of the expiry comes : + * K / F ^ (expiry2 / L) - K = K / F ^ (expiry1 / L) - K + I + * which resolves to : + * expiry2 = L * logF(K / (I + K / F ^ (expiry1 / L))) + * + * In this implementation, the bump is defined as 1/25th of the cap for + * the relevance. This means a network will be remembered for the maximum + * period of 400 days if connected 25 times in succession not accounting + * for decay. Of course decay actually happens so it will take more than 25 + * connections for any given network to actually reach the cap, but because + * decay is slow at first, it is a good estimate of how fast cap happens. + * + * Specifically, it gives the following four results : + * - A network that a device connects to once hits irrelevance about 32.7 days after + * it was first registered if never connected again. + * - A network that a device connects to once a day at a fixed hour will hit the cap + * on the 27th connection. + * - A network that a device connects to once a week at a fixed hour will hit the cap + * on the 57th connection. + * - A network that a device connects to every day for 7 straight days then never again + * expires 144 days after the last connection. + * These metrics tend to match pretty well the requirements. + */ + + // TODO : make these constants configurable at runtime. Don't forget to build it so that + // changes will wipe the database, migrate the values, or otherwise make sure the relevance + // values are still meaningful. + + // How long, in milliseconds, is a capped relevance valid for, or in other + // words how many milliseconds after its relevance was set to RELEVANCE_CAP does + // any given line expire. 400 days. + @VisibleForTesting + public static final long CAPPED_RELEVANCE_LIFETIME_MS = 400L * 24 * 60 * 60 * 1000; + + // The constant that represents a normalized 1.0 value for the relevance. In other words, + // the cap for the relevance. This is referred to as M in the explanation above. + @VisibleForTesting + public static final int CAPPED_RELEVANCE = 1_000_000; + + // The decay factor. After a half-life, the relevance will have decayed by this value. + // This is referred to as F in the explanation above. + private static final double DECAY_FACTOR = 0.5; + + // The half-life. After this time, the relevance will have decayed by a factor DECAY_FACTOR. + // This is referred to as L in the explanation above. + private static final long HALF_LIFE_MS = 200L * 24 * 60 * 60 * 1000; + + // The value of the frame change. This is referred to as K in the explanation above. + private static final double IRRELEVANCE_FLOOR = + CAPPED_RELEVANCE * powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS) + / (1 - powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS)); + + // How much to bump the relevance by every time a line is written to. + @VisibleForTesting + public static final int RELEVANCE_BUMP = CAPPED_RELEVANCE / 25; + + // Java doesn't include a function for the logarithm in an arbitrary base, so implement it + private static final double LOG_DECAY_FACTOR = Math.log(DECAY_FACTOR); + private static double logF(final double value) { + return Math.log(value) / LOG_DECAY_FACTOR; + } + + // Utility function to get a power of the decay factor, to simplify the code. + private static double powF(final double value) { + return Math.pow(DECAY_FACTOR, value); + } + + /** + * Compute the value of the relevance now given an expiry date. + * + * @param expiry the date at which the column in the database expires. + * @return the adjusted value of the relevance for this moment in time. + */ + public static int computeRelevanceForNow(final long expiry) { + return computeRelevanceForTargetDate(expiry, System.currentTimeMillis()); + } + + /** + * Compute the value of the relevance at a given date from an expiry date. + * + * Because relevance decays with time, a relevance in the past corresponds to + * a different relevance later. + * + * Relevance is always a positive value. 0 means not relevant at all. + * + * See the explanation at the top of this file to get the justification for this + * computation. + * + * @param expiry the date at which the column in the database expires. + * @param target the target date to adjust the relevance to. + * @return the adjusted value of the relevance for the target moment. + */ + public static int computeRelevanceForTargetDate(final long expiry, final long target) { + final long delay = expiry - target; + if (delay >= CAPPED_RELEVANCE_LIFETIME_MS) return CAPPED_RELEVANCE; + if (delay <= 0) return 0; + return (int) (IRRELEVANCE_FLOOR / powF((float) delay / HALF_LIFE_MS) - IRRELEVANCE_FLOOR); + } + + /** + * Compute the expiry duration adjusted up for a new fresh write. + * + * Every time data is written to the memory store for a given line, the + * relevance is bumped up by a certain amount, which will boost the priority + * of this line for computation of group attributes, and delay (possibly + * indefinitely, if the line is accessed regularly) forgetting the data stored + * in that line. + * As opposed to bumpExpiryDate, this function uses a duration from now to expiry. + * + * See the explanation at the top of this file for a justification of this computation. + * + * @param oldExpiryDuration the old expiry duration in milliseconds from now. + * @return the expiry duration representing a bumped up relevance value. + */ + public static long bumpExpiryDuration(final long oldExpiryDuration) { + // L * logF(K / (I + K / F ^ (expiry1 / L))), as documented above + final double divisionFactor = powF(((double) oldExpiryDuration) / HALF_LIFE_MS); + final double oldRelevance = IRRELEVANCE_FLOOR / divisionFactor; + final long newDuration = + (long) (HALF_LIFE_MS * logF(IRRELEVANCE_FLOOR / (RELEVANCE_BUMP + oldRelevance))); + return Math.min(newDuration, CAPPED_RELEVANCE_LIFETIME_MS); + } + + /** + * Compute the new expiry date adjusted up for a new fresh write. + * + * Every time data is written to the memory store for a given line, the + * relevance is bumped up by a certain amount, which will boost the priority + * of this line for computation of group attributes, and delay (possibly + * indefinitely, if the line is accessed regularly) forgetting the data stored + * in that line. + * As opposed to bumpExpiryDuration, this function takes the old timestamp and returns the + * new timestamp. + * + * {@see bumpExpiryDuration}, and keep in mind that the bump depends on when this is called, + * because the relevance decays exponentially, therefore bumping up a high relevance (for a + * date far in the future) is less potent than bumping up a low relevance (for a date in + * a close future). + * + * @param oldExpiryDate the old date of expiration. + * @return the new expiration date after the relevance bump. + */ + public static long bumpExpiryDate(final long oldExpiryDate) { + final long now = System.currentTimeMillis(); + final long newDuration = bumpExpiryDuration(oldExpiryDate - now); + return now + newDuration; + } +} diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java new file mode 100644 index 000000000000..5b77f543c62b --- /dev/null +++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java @@ -0,0 +1,222 @@ +/* + * 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.shared; + +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + +import android.annotation.Nullable; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.IpPrefixParcelable; +import android.net.LinkAddress; +import android.net.LinkAddressParcelable; +import android.net.LinkProperties; +import android.net.LinkPropertiesParcelable; +import android.net.ProxyInfo; +import android.net.ProxyInfoParcelable; +import android.net.RouteInfo; +import android.net.RouteInfoParcelable; +import android.net.Uri; + +import java.net.InetAddress; +import java.util.Arrays; + +/** + * Collection of utility methods to convert to and from stable AIDL parcelables for LinkProperties + * and its attributes. + * @hide + */ +public final class LinkPropertiesParcelableUtil { + + /** + * Convert a ProxyInfo to a ProxyInfoParcelable + */ + public static ProxyInfoParcelable toStableParcelable(@Nullable ProxyInfo proxyInfo) { + if (proxyInfo == null) { + return null; + } + final ProxyInfoParcelable parcel = new ProxyInfoParcelable(); + parcel.host = proxyInfo.getHost(); + parcel.port = proxyInfo.getPort(); + parcel.exclusionList = proxyInfo.getExclusionList(); + parcel.pacFileUrl = proxyInfo.getPacFileUrl().toString(); + return parcel; + } + + /** + * Convert a ProxyInfoParcelable to a ProxyInfo + */ + public static ProxyInfo fromStableParcelable(@Nullable ProxyInfoParcelable parcel) { + if (parcel == null) { + return null; + } + if (Uri.EMPTY.toString().equals(parcel.pacFileUrl)) { + return ProxyInfo.buildDirectProxy( + parcel.host, parcel.port, Arrays.asList(parcel.exclusionList)); + } else { + return ProxyInfo.buildPacProxy(Uri.parse(parcel.pacFileUrl)); + } + } + + /** + * Convert an IpPrefixParcelable to an IpPrefix + */ + public static IpPrefixParcelable toStableParcelable(@Nullable IpPrefix ipPrefix) { + if (ipPrefix == null) { + return null; + } + final IpPrefixParcelable parcel = new IpPrefixParcelable(); + parcel.address = ipPrefix.getAddress().getHostAddress(); + parcel.prefixLength = ipPrefix.getPrefixLength(); + return parcel; + } + + /** + * Convert an IpPrefix to an IpPrefixParcelable + */ + public static IpPrefix fromStableParcelable(@Nullable IpPrefixParcelable parcel) { + if (parcel == null) { + return null; + } + return new IpPrefix(InetAddresses.parseNumericAddress(parcel.address), parcel.prefixLength); + } + + /** + * Convert a RouteInfoParcelable to a RouteInfo + */ + public static RouteInfoParcelable toStableParcelable(@Nullable RouteInfo routeInfo) { + if (routeInfo == null) { + return null; + } + final RouteInfoParcelable parcel = new RouteInfoParcelable(); + parcel.destination = toStableParcelable(routeInfo.getDestination()); + parcel.gatewayAddr = routeInfo.getGateway().getHostAddress(); + parcel.ifaceName = routeInfo.getInterface(); + parcel.type = routeInfo.getType(); + return parcel; + } + + /** + * Convert a RouteInfo to a RouteInfoParcelable + */ + public static RouteInfo fromStableParcelable(@Nullable RouteInfoParcelable parcel) { + if (parcel == null) { + return null; + } + final IpPrefix destination = fromStableParcelable(parcel.destination); + return new RouteInfo( + destination, InetAddresses.parseNumericAddress(parcel.gatewayAddr), + parcel.ifaceName, parcel.type); + } + + /** + * Convert a LinkAddressParcelable to a LinkAddress + */ + public static LinkAddressParcelable toStableParcelable(@Nullable LinkAddress la) { + if (la == null) { + return null; + } + final LinkAddressParcelable parcel = new LinkAddressParcelable(); + parcel.address = la.getAddress().getHostAddress(); + parcel.prefixLength = la.getPrefixLength(); + parcel.flags = la.getFlags(); + parcel.scope = la.getScope(); + return parcel; + } + + /** + * Convert a LinkAddress to a LinkAddressParcelable + */ + public static LinkAddress fromStableParcelable(@Nullable LinkAddressParcelable parcel) { + if (parcel == null) { + return null; + } + return new LinkAddress( + InetAddresses.parseNumericAddress(parcel.address), + parcel.prefixLength, + parcel.flags, + parcel.scope); + } + + /** + * Convert a LinkProperties to a LinkPropertiesParcelable + */ + public static LinkPropertiesParcelable toStableParcelable(@Nullable LinkProperties lp) { + if (lp == null) { + return null; + } + final LinkPropertiesParcelable parcel = new LinkPropertiesParcelable(); + parcel.ifaceName = lp.getInterfaceName(); + parcel.linkAddresses = toParcelableArray( + lp.getLinkAddresses(), + LinkPropertiesParcelableUtil::toStableParcelable, + LinkAddressParcelable.class); + parcel.dnses = toParcelableArray( + lp.getDnsServers(), InetAddress::getHostAddress, String.class); + parcel.pcscfs = toParcelableArray( + lp.getPcscfServers(), InetAddress::getHostAddress, String.class); + parcel.validatedPrivateDnses = toParcelableArray( + lp.getValidatedPrivateDnsServers(), InetAddress::getHostAddress, String.class); + parcel.usePrivateDns = lp.isPrivateDnsActive(); + parcel.privateDnsServerName = lp.getPrivateDnsServerName(); + parcel.domains = lp.getDomains(); + parcel.routes = toParcelableArray( + lp.getRoutes(), LinkPropertiesParcelableUtil::toStableParcelable, + RouteInfoParcelable.class); + parcel.httpProxy = toStableParcelable(lp.getHttpProxy()); + parcel.mtu = lp.getMtu(); + parcel.tcpBufferSizes = lp.getTcpBufferSizes(); + parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix()); + parcel.stackedLinks = toParcelableArray( + lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable, + LinkPropertiesParcelable.class); + return parcel; + } + + /** + * Convert a LinkPropertiesParcelable to a LinkProperties + */ + public static LinkProperties fromStableParcelable(@Nullable LinkPropertiesParcelable parcel) { + if (parcel == null) { + return null; + } + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(parcel.ifaceName); + lp.setLinkAddresses(fromParcelableArray(parcel.linkAddresses, + LinkPropertiesParcelableUtil::fromStableParcelable)); + lp.setDnsServers(fromParcelableArray(parcel.dnses, InetAddresses::parseNumericAddress)); + lp.setPcscfServers(fromParcelableArray(parcel.pcscfs, InetAddresses::parseNumericAddress)); + lp.setValidatedPrivateDnsServers( + fromParcelableArray(parcel.validatedPrivateDnses, + InetAddresses::parseNumericAddress)); + lp.setUsePrivateDns(parcel.usePrivateDns); + lp.setPrivateDnsServerName(parcel.privateDnsServerName); + lp.setDomains(parcel.domains); + for (RouteInfoParcelable route : parcel.routes) { + lp.addRoute(fromStableParcelable(route)); + } + lp.setHttpProxy(fromStableParcelable(parcel.httpProxy)); + lp.setMtu(parcel.mtu); + lp.setTcpBufferSizes(parcel.tcpBufferSizes); + lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix)); + for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) { + lp.addStackedLink(fromStableParcelable(stackedLink)); + } + return lp; + } +} diff --git a/services/net/java/android/net/shared/ParcelableUtil.java b/services/net/java/android/net/shared/ParcelableUtil.java new file mode 100644 index 000000000000..a18976c9eee6 --- /dev/null +++ b/services/net/java/android/net/shared/ParcelableUtil.java @@ -0,0 +1,63 @@ +/* + * 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.shared; + +import android.annotation.NonNull; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** + * Utility methods to help convert to/from stable parcelables. + * @hide + */ +public final class ParcelableUtil { + // Below methods could be implemented easily with streams, but streams are frowned upon in + // frameworks code. + + /** + * Convert a list of BaseType items to an array of ParcelableType items using the specified + * converter function. + */ + public static <ParcelableType, BaseType> ParcelableType[] toParcelableArray( + @NonNull List<BaseType> base, + @NonNull Function<BaseType, ParcelableType> conv, + @NonNull Class<ParcelableType> parcelClass) { + final ParcelableType[] out = (ParcelableType[]) Array.newInstance(parcelClass, base.size()); + int i = 0; + for (BaseType b : base) { + out[i] = conv.apply(b); + i++; + } + return out; + } + + /** + * Convert an array of ParcelableType items to a list of BaseType items using the specified + * converter function. + */ + public static <ParcelableType, BaseType> ArrayList<BaseType> fromParcelableArray( + @NonNull ParcelableType[] parceled, @NonNull Function<ParcelableType, BaseType> conv) { + final ArrayList<BaseType> out = new ArrayList<>(parceled.length); + for (ParcelableType t : parceled) { + out.add(conv.apply(t)); + } + return out; + } +} diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java index 7559560f4f6d..9a6e003c1317 100644 --- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java @@ -58,7 +58,9 @@ import com.android.server.backup.transport.OnTransportRegisteredListener; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportClientManager; import com.android.server.backup.transport.TransportNotRegisteredException; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,6 +68,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowPackageManager; import java.util.ArrayList; @@ -75,6 +78,7 @@ import java.util.Set; import java.util.stream.Stream; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowApplicationPackageManager.class}) @Presubmit public class TransportManagerTest { private static final String PACKAGE_A = "some.package.a"; @@ -102,6 +106,12 @@ public class TransportManagerTest { mTransportB1 = genericTransport(PACKAGE_B, "TransportBaz"); } + /** Reset shadow state. */ + @After + public void tearDown() throws Exception { + ShadowApplicationPackageManager.reset(); + } + @Test public void testRegisterTransports() throws Exception { setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); @@ -666,7 +676,8 @@ public class TransportManagerTest { packageInfo.packageName = packageName; packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.privateFlags = flags; - mShadowPackageManager.addPackage(packageInfo); + mShadowPackageManager.installPackage(packageInfo); + ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo); } private TransportManager createTransportManagerWithRegisteredTransports( 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 3b7fa3d6ed05..427aed7364d2 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -514,10 +514,14 @@ public class UserBackupManagerServiceTest { private void setUpForUpdateTransportAttributes() throws Exception { mTransportComponent = mTransport.getTransportComponent(); String transportPackage = mTransportComponent.getPackageName(); + PackageInfo packageInfo = getPackageInfo(transportPackage); ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager()); - shadowPackageManager.addPackage(transportPackage); + shadowPackageManager.installPackage(packageInfo); shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage); + // Set up for user invocations on ApplicationPackageManager. + ShadowApplicationPackageManager.addInstalledPackage(transportPackage, packageInfo); + ShadowApplicationPackageManager.setPackageUid(transportPackage, PACKAGE_UID); mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0); } diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java index dc322094add8..ab121eddff06 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java @@ -38,6 +38,7 @@ public class ShadowApplicationPackageManager extends org.robolectric.shadows.ShadowApplicationPackageManager { private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>(); private static final List<PackageInfo> sInstalledPackages = new ArrayList<>(); + private static final Map<String, Integer> sPackageUids = new ArrayMap<>(); /** * Registers the package {@code packageName} to be returned when invoking {@link @@ -49,6 +50,14 @@ public class ShadowApplicationPackageManager sInstalledPackages.add(packageInfo); } + /** + * Sets the package uid {@code packageUid} for the package {@code packageName} to be returned + * when invoking {@link ApplicationPackageManager#getPackageUidAsUser(String, int, int)}. + */ + public static void setPackageUid(String packageName, int packageUid) { + sPackageUids.put(packageName, packageUid); + } + @Override protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) throws NameNotFoundException { @@ -63,6 +72,15 @@ public class ShadowApplicationPackageManager return sInstalledPackages; } + @Override + protected int getPackageUidAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { + if (!sPackageUids.containsKey(packageName)) { + throw new NameNotFoundException(packageName); + } + return sPackageUids.get(packageName); + } + /** Clear package state. */ @Resetter public static void reset() { diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index c8e67820ce46..4a48468867df 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -85,6 +85,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -793,6 +794,7 @@ public class AppStateTrackerTest { } @Test + @FlakyTest(bugId = 114098433) public void testAllListeners() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index f1cd0cd6d30c..57ee6dcad9f2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -43,7 +44,12 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AlarmManager; +import android.app.AppGlobals; +import android.app.IActivityManager; +import android.app.IUidObserver; import android.app.job.JobInfo; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; @@ -56,13 +62,16 @@ import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.os.SystemClock; +import android.util.SparseBooleanArray; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; +import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.TimingSession; @@ -96,9 +105,13 @@ public class QuotaControllerTest { private BroadcastReceiver mChargingReceiver; private Constants mConstants; private QuotaController mQuotaController; + private int mSourceUid; + private IUidObserver mUidObserver; private MockitoSession mMockingSession; @Mock + private ActivityManagerInternal mActivityMangerInternal; + @Mock private AlarmManager mAlarmManager; @Mock private Context mContext; @@ -107,6 +120,8 @@ public class QuotaControllerTest { @Mock private UsageStatsManagerInternal mUsageStatsManager; + private JobStore mJobStore; + @Before public void setUp() { mMockingSession = mockitoSession() @@ -123,8 +138,17 @@ public class QuotaControllerTest { when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); when(mJobSchedulerService.getConstants()).thenReturn(mConstants); // Called in QuotaController constructor. + IActivityManager activityManager = ActivityManager.getService(); + spyOn(activityManager); + try { + doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); + } catch (RemoteException e) { + fail("registerUidObserver threw exception: " + e.getMessage()); + } when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); + doReturn(mActivityMangerInternal) + .when(() -> LocalServices.getService(ActivityManagerInternal.class)); doReturn(mock(BatteryManagerInternal.class)) .when(() -> LocalServices.getService(BatteryManagerInternal.class)); doReturn(mUsageStatsManager) @@ -132,6 +156,9 @@ public class QuotaControllerTest { // Used in JobStatus. doReturn(mock(PackageManagerInternal.class)) .when(() -> LocalServices.getService(PackageManagerInternal.class)); + // Used in QuotaController.Handler. + mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir()); + when(mJobSchedulerService.getJobStore()).thenReturn(mJobStore); // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions // in the past, and QuotaController sometimes floors values at 0, so if the test time @@ -150,10 +177,23 @@ public class QuotaControllerTest { // Capture the listeners. ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); + ArgumentCaptor<IUidObserver> uidObserverCaptor = + ArgumentCaptor.forClass(IUidObserver.class); mQuotaController = new QuotaController(mJobSchedulerService); verify(mContext).registerReceiver(receiverCaptor.capture(), any()); mChargingReceiver = receiverCaptor.getValue(); + try { + verify(activityManager).registerUidObserver( + uidObserverCaptor.capture(), + eq(ActivityManager.UID_OBSERVER_PROCSTATE), + eq(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE), + any()); + mUidObserver = uidObserverCaptor.getValue(); + mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0); + } catch (RemoteException e) { + fail(e.getMessage()); + } } @After @@ -182,6 +222,25 @@ public class QuotaControllerTest { mChargingReceiver.onReceive(mContext, intent); } + private void setProcessState(int procState) { + try { + doReturn(procState).when(mActivityMangerInternal).getUidProcessState(mSourceUid); + SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids(); + spyOn(foregroundUids); + mUidObserver.onUidStateChanged(mSourceUid, procState, 0); + if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)) + .put(eq(mSourceUid), eq(true)); + assertTrue(foregroundUids.get(mSourceUid)); + } else { + verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)).delete(eq(mSourceUid)); + assertFalse(foregroundUids.get(mSourceUid)); + } + } catch (RemoteException e) { + fail("registerUidObserver threw exception: " + e.getMessage()); + } + } + private void setStandbyBucket(int bucketIndex) { int bucket; switch (bucketIndex) { @@ -204,9 +263,18 @@ public class QuotaControllerTest { anyLong())).thenReturn(bucket); } - private void setStandbyBucket(int bucketIndex, JobStatus job) { + private void setStandbyBucket(int bucketIndex, JobStatus... jobs) { setStandbyBucket(bucketIndex); - job.setStandbyBucket(bucketIndex); + for (JobStatus job : jobs) { + job.setStandbyBucket(bucketIndex); + } + } + + private void trackJobs(JobStatus... jobs) { + for (JobStatus job : jobs) { + mJobStore.add(job); + mQuotaController.maybeStartTrackingJobLocked(job, null); + } } private JobStatus createJobStatus(String testTag, int jobId) { @@ -214,8 +282,11 @@ public class QuotaControllerTest { new ComponentName(mContext, "TestQuotaJobService")) .setMinimumLatency(Math.abs(jobId) + 1) .build(); - return JobStatus.createFromJobInfo( + JobStatus js = JobStatus.createFromJobInfo( jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag); + // Make sure tests aren't passing just because the default bucket is likely ACTIVE. + js.setStandbyBucket(FREQUENT_INDEX); + return js; } private TimingSession createTimingSession(long start, long duration, int count) { @@ -709,6 +780,7 @@ public class QuotaControllerTest { verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1); + setStandbyBucket(standbyBucket, jobStatus); mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); mQuotaController.prepareForExecutionLocked(jobStatus); advanceElapsedClock(5 * MINUTE_IN_MILLIS); @@ -1339,19 +1411,23 @@ public class QuotaControllerTest { setDischarging(); JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1); - jobStatus.uidActive = true; + setProcessState(ActivityManager.PROCESS_STATE_TOP); mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); mQuotaController.prepareForExecutionLocked(jobStatus); advanceElapsedClock(5 * SECOND_IN_MILLIS); + // Change to a state that should still be considered foreground. + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + advanceElapsedClock(5 * SECOND_IN_MILLIS); mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } /** - * Tests that Timers properly track overlapping foreground and background jobs. + * Tests that Timers properly track sessions when switching between foreground and background + * states. */ @Test public void testTimerTracking_ForegroundAndBackground() { @@ -1360,7 +1436,6 @@ public class QuotaControllerTest { JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1); JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2); JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3); - jobFg3.uidActive = true; mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); @@ -1368,6 +1443,7 @@ public class QuotaControllerTest { List<TimingSession> expected = new ArrayList<>(); // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); long start = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.prepareForExecutionLocked(jobBg1); advanceElapsedClock(10 * SECOND_IN_MILLIS); @@ -1379,48 +1455,223 @@ public class QuotaControllerTest { // Bg job starts while inactive, spans an entire active session, and ends after the // active session. - // Fg job starts after the bg job and ends before the bg job. - // Entire bg job duration should be counted since it started before active session. However, - // count should only be 1 since Timer shouldn't count fg jobs. + // App switching to foreground state then fg job starts. + // App remains in foreground state after coming to foreground, so there should only be one + // session. start = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); mQuotaController.prepareForExecutionLocked(jobBg2); advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); mQuotaController.prepareForExecutionLocked(jobFg3); advanceElapsedClock(10 * SECOND_IN_MILLIS); mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); advanceElapsedClock(10 * SECOND_IN_MILLIS); mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); - expected.add(createTimingSession(start, 30 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); advanceElapsedClock(SECOND_IN_MILLIS); // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes // "inactive" and then bg job 2 starts. Then fg job ends. - // This should result in two TimingSessions with a count of one each. + // This should result in two TimingSessions: + // * The first should have a count of 1 + // * The second should have a count of 2 since it will include both jobs start = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); mQuotaController.prepareForExecutionLocked(jobBg1); advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); mQuotaController.prepareForExecutionLocked(jobFg3); advanceElapsedClock(10 * SECOND_IN_MILLIS); mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); - expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); mQuotaController.prepareForExecutionLocked(jobBg2); advanceElapsedClock(10 * SECOND_IN_MILLIS); mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); advanceElapsedClock(10 * SECOND_IN_MILLIS); mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); - expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } /** + * Tests that Timers properly track overlapping top and background jobs. + */ + @Test + public void testTimerTracking_TopAndNonTop() { + setDischarging(); + + JobStatus jobBg1 = createJobStatus("testTimerTracking_TopAndNonTop", 1); + JobStatus jobBg2 = createJobStatus("testTimerTracking_TopAndNonTop", 2); + JobStatus jobFg1 = createJobStatus("testTimerTracking_TopAndNonTop", 3); + JobStatus jobTop = createJobStatus("testTimerTracking_TopAndNonTop", 4); + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.prepareForExecutionLocked(jobBg1); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Bg job starts while inactive, spans an entire active session, and ends after the + // active session. + // App switching to top state then fg job starts. + // App remains in top state after coming to top, so there should only be one + // session. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.prepareForExecutionLocked(jobBg2); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + mQuotaController.prepareForExecutionLocked(jobTop); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to + // foreground_service and a new job starts. Shortly after, uid goes + // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. + // This should result in two TimingSessions: + // * The first should have a count of 1 + // * The second should have a count of 2, which accounts for the bg2 and fg, but not top + // jobs. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); + mQuotaController.prepareForExecutionLocked(jobBg1); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + mQuotaController.prepareForExecutionLocked(jobTop); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + mQuotaController.prepareForExecutionLocked(jobFg1); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + mQuotaController.prepareForExecutionLocked(jobBg2); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false); + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that TOP jobs aren't stopped when an app runs out of quota. + */ + @Test + public void testTracking_OutOfQuota_ForegroundAndBackground() { + setDischarging(); + + JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1); + JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2); + trackJobs(jobBg, jobTop); + setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window + // Now the package only has 20 seconds to run. + final long remainingTimeMs = 20 * SECOND_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, + 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1)); + + InOrder inOrder = inOrder(mJobSchedulerService); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + // Start the job. + mQuotaController.prepareForExecutionLocked(jobBg); + advanceElapsedClock(remainingTimeMs / 2); + // New job starts after UID is in the foreground. Since the app is now in the foreground, it + // should continue to have remainingTimeMs / 2 time remaining. + setProcessState(ActivityManager.PROCESS_STATE_TOP); + mQuotaController.prepareForExecutionLocked(jobTop); + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) + .onControllerStateChanged(); + assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobBg)); + assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobTop)); + // Go to a background state. + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + advanceElapsedClock(remainingTimeMs / 2 + 1); + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + // Top job should still be allowed to run. + assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + // New jobs to run. + JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3); + JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4); + JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5); + setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + trackJobs(jobFg, jobTop); + mQuotaController.prepareForExecutionLocked(jobTop); + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + // App still in foreground so everything should be in quota. + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + // App is now in background and out of quota. Fg should now change to out of quota since it + // wasn't started. Top should remain in quota since it started when the app was in TOP. + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + trackJobs(jobBg2); + assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + } + + /** * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches * its quota. */ diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java index 8b0e8abf069d..eb9b98ec65f7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java @@ -30,6 +30,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -147,6 +148,7 @@ public class ArcInitiationActionFromAvrTest { mTestLooper.dispatchAll(); } + @Ignore("b/120845532") @Test public void arcInitiation_requestActiveSource() { mSendCecCommandSuccess = true; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java index 93a09dc3ff04..5d8131f35eb7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java @@ -40,7 +40,6 @@ import androidx.test.filters.SmallTest; import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -142,7 +141,6 @@ public class HdmiCecControllerTest { assertEquals(ADDR_UNREGISTERED, mLogicalAddress); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") @Test public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback); @@ -158,7 +156,6 @@ public class HdmiCecControllerTest { assertEquals(ADDR_PLAYBACK_2, mLogicalAddress); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") @Test public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() { mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 3b51a2a70f83..3a6cdc2ad7c9 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -21,6 +21,7 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVIC import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_TUNER_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -29,9 +30,9 @@ import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; import static com.google.common.truth.Truth.assertThat; import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.HdmiPortInfo; import android.media.AudioManager; import android.os.Looper; -import android.os.SystemProperties; import android.os.test.TestLooper; import androidx.test.InstrumentationRegistry; @@ -46,7 +47,6 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.ArrayList; - @SmallTest @RunWith(JUnit4.class) /** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */ @@ -67,9 +67,15 @@ public class HdmiCecLocalDeviceAudioSystemTest { private int mMusicVolume; private int mMusicMaxVolume; private boolean mMusicMute; - private int mAvrPhysicalAddress; + private static final int SELF_PHYSICAL_ADDRESS = 0x2000; + private static final int HDMI_1_PHYSICAL_ADDRESS = 0x2100; + private static final int HDMI_2_PHYSICAL_ADDRESS = 0x2200; + private static final int HDMI_3_PHYSICAL_ADDRESS = 0x2300; private int mInvokeDeviceEventState; private HdmiDeviceInfo mDeviceInfo; + private boolean mMutingEnabled; + private boolean mArcSupport; + private HdmiPortInfo[] mHdmiPortInfo; @Before public void setUp() { @@ -141,9 +147,20 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Override - int pathToPortId(int path) { - // port id is not useful for the test right now - return 1; + void writeStringSetting(String key, String value) { + // do nothing + } + + @Override + boolean readBooleanSetting(String key, boolean defVal) { + switch (key) { + case Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE: + return mMutingEnabled; + case Constants.PROPERTY_ARC_SUPPORT: + return mArcSupport; + default: + return defVal; + } } }; @@ -154,11 +171,17 @@ public class HdmiCecLocalDeviceAudioSystemTest { void setIsActiveSource(boolean on) { mIsActiveSource = on; } + + @Override + protected int getPreferredAddress() { + return ADDR_PLAYBACK_1; + } }; mHdmiCecLocalDeviceAudioSystem.init(); mHdmiCecLocalDevicePlayback.init(); mHdmiControlService.setIoLooper(mMyLooper); mNativeWrapper = new FakeNativeWrapper(); + mNativeWrapper.setPhysicalAddress(SELF_PHYSICAL_ADDRESS); mHdmiCecController = HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper); mHdmiControlService.setCecController(mHdmiCecController); @@ -166,15 +189,28 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem); mLocalDevices.add(mHdmiCecLocalDevicePlayback); + mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true); + mHdmiPortInfo = new HdmiPortInfo[4]; + mHdmiPortInfo[0] = + new HdmiPortInfo( + 0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS, true, false, false); + mHdmiPortInfo[1] = + new HdmiPortInfo( + 2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS, true, false, false); + mHdmiPortInfo[2] = + new HdmiPortInfo( + 1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS, true, false, false); + mHdmiPortInfo[3] = + new HdmiPortInfo( + 4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false); + mNativeWrapper.setPortInfo(mHdmiPortInfo); mHdmiControlService.initPortInfo(); // No TV device interacts with AVR so system audio control won't be turned on here mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); mNativeWrapper.clearResultMessages(); - mAvrPhysicalAddress = 0x2000; - mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress); - SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true"); - SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true"); + mMutingEnabled = true; + mArcSupport = true; mInvokeDeviceEventState = 0; mDeviceInfo = null; } @@ -244,6 +280,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } + // Testing device has sadConfig.xml + @Ignore("b/120845532") @Test public void handleRequestShortAudioDescriptor_noAudioDeviceInfo() throws Exception { HdmiCecMessage expectedMessage = @@ -414,51 +452,6 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void pathToPort_isMe() throws Exception { - int targetPhysicalAddress = 0x1000; - mNativeWrapper.setPhysicalAddress(0x1000); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(0); - } - - @Test - public void pathToPort_isBelow() throws Exception { - int targetPhysicalAddress = 0x1100; - mNativeWrapper.setPhysicalAddress(0x1000); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(1); - } - - @Test - public void pathToPort_neitherMeNorBelow() throws Exception { - int targetPhysicalAddress = 0x3000; - mNativeWrapper.setPhysicalAddress(0x2000); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - - targetPhysicalAddress = 0x2200; - mNativeWrapper.setPhysicalAddress(0x3300); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - - targetPhysicalAddress = 0x2213; - mNativeWrapper.setPhysicalAddress(0x2212); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - - targetPhysicalAddress = 0x2340; - mNativeWrapper.setPhysicalAddress(0x2310); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - } - - @Test public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception { HdmiCecMessage message = HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); @@ -530,7 +523,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { ADDR_TV, Constants.MESSAGE_REQUEST_ARC_INITIATION, Constants.ABORT_UNRECOGNIZED_OPCODE); - SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false"); + mArcSupport = false; assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue(); mTestLooper.dispatchAll(); @@ -541,7 +534,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { HdmiCecMessage message = HdmiCecMessageBuilder.buildSystemAudioModeRequest( ADDR_TUNER_1, ADDR_AUDIO_SYSTEM, - mAvrPhysicalAddress, true); + SELF_PHYSICAL_ADDRESS, true); HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( ADDR_AUDIO_SYSTEM, @@ -559,7 +552,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { HdmiCecMessage message = HdmiCecMessageBuilder.buildSystemAudioModeRequest( ADDR_TUNER_1, ADDR_AUDIO_SYSTEM, - mAvrPhysicalAddress, true); + SELF_PHYSICAL_ADDRESS, true); HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildSetSystemAudioMode( ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, true); @@ -585,15 +578,14 @@ public class HdmiCecLocalDeviceAudioSystemTest { .isEqualTo(expectedActiveSource); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") @Test public void handleRoutingChange_currentActivePortIsHome() { HdmiCecMessage message = - HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, mAvrPhysicalAddress); + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, mAvrPhysicalAddress); - ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, mAvrPhysicalAddress); + HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS); + ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS); int expectedLocalActivePort = Constants.CEC_SWITCH_HOME; assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue(); @@ -608,17 +600,18 @@ public class HdmiCecLocalDeviceAudioSystemTest { @Test public void handleRoutingInformation_currentActivePortIsHDMI1() { HdmiCecMessage message = - HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x2000); - mHdmiCecLocalDeviceAudioSystem.setRoutingPort(Constants.CEC_SWITCH_HDMI1); + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, SELF_PHYSICAL_ADDRESS); + mHdmiCecLocalDeviceAudioSystem.setRoutingPort(mHdmiPortInfo[1].getId()); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x2100); + HdmiCecMessageBuilder.buildRoutingInformation( + ADDR_AUDIO_SYSTEM, HDMI_1_PHYSICAL_ADDRESS); assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingInformation(message)).isTrue(); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") + @Ignore("b/120845532") @Test public void handleRoutingChange_homeIsActive_playbackSendActiveSource() { HdmiCecMessage message = @@ -667,7 +660,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice); HdmiDeviceInfo differentDevice = new HdmiDeviceInfo( - ADDR_PLAYBACK_1, 0x2100, 4, HdmiDeviceInfo.DEVICE_PLAYBACK, + ADDR_PLAYBACK_1, 0x2300, 4, HdmiDeviceInfo.DEVICE_PLAYBACK, Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1)); mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice); @@ -686,14 +679,13 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice); HdmiDeviceInfo differentDevice = new HdmiDeviceInfo( - ADDR_PLAYBACK_1, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK, - Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1)); + ADDR_PLAYBACK_2, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_2)); HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder .buildReportPhysicalAddressCommand( - ADDR_PLAYBACK_1, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK); + ADDR_PLAYBACK_2, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK); mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress); - mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice); mTestLooper.dispatchAll(); assertThat(mDeviceInfo).isEqualTo(differentDevice); assertThat(mHdmiCecLocalDeviceAudioSystem diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 792c617b99ea..feae4eed7eb1 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -81,7 +81,8 @@ public class HdmiCecLocalDevicePlaybackTest { mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress); } - @Ignore + // Playback device does not handle routing control related feature right now + @Ignore("b/120845532") @Test public void handleSetStreamPath_underCurrentDevice() { assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 67ce13fdef72..1f660742122d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -179,6 +179,7 @@ public class HdmiControlServiceTest { @Test public void pathToPort_pathExists_weAreNonTv() { mNativeWrapper.setPhysicalAddress(0x2000); + mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1); assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2); } @@ -186,6 +187,7 @@ public class HdmiControlServiceTest { @Test public void pathToPort_pathExists_weAreTv() { mNativeWrapper.setPhysicalAddress(0x0000); + mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3); assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4); } @@ -193,6 +195,7 @@ public class HdmiControlServiceTest { @Test public void pathToPort_pathInvalid() { mNativeWrapper.setPhysicalAddress(0x2000); + mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java new file mode 100644 index 000000000000..985c6476d767 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java @@ -0,0 +1,146 @@ +/* + * 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.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import android.util.Slog; + +import androidx.test.filters.SmallTest; + +import com.android.server.hdmi.HdmiUtils.CodecSad; +import com.android.server.hdmi.HdmiUtils.DeviceConfig; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(JUnit4.class) +/** Tests for {@link HdmiUtils} class. */ +public class HdmiUtilsTest { + + private static final String TAG = "HdmiUtilsTest"; + + private final String mExampleXML = + "<!-- A sample Short Audio Descriptor configuration xml -->" + + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">" + + "<device type=\"VX_AUDIO_DEVICE_IN_HDMI_ARC\">" + + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"011a03\"/>" + + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"0d0506\"/>" + + "</device>" + + "<device type=\"AUDIO_DEVICE_IN_SPDIF\">" + + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"010203\"/>" + + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"040506\"/>" + + "</device>" + + "</config>"; + + @Test + public void pathToPort_isMe() { + int targetPhysicalAddress = 0x1000; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS); + } + + @Test + public void pathToPort_isDirectlyBelow() { + int targetPhysicalAddress = 0x1100; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_isBelow() { + int targetPhysicalAddress = 0x1110; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_neitherMeNorBelow() { + int targetPhysicalAddress = 0x3000; + int myPhysicalAddress = 0x2000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2200; + myPhysicalAddress = 0x3300; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2213; + myPhysicalAddress = 0x2212; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2340; + myPhysicalAddress = 0x2310; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + } + + @Test + public void parseSampleXML() { + List<DeviceConfig> config = new ArrayList<>(); + try { + config = HdmiUtils.ShortAudioDescriptorXmlParser.parse( + new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8))); + } catch (IOException e) { + Slog.e(TAG, e.getMessage(), e); + } catch (XmlPullParserException e) { + Slog.e(TAG, e.getMessage(), e); + } + + CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03"); + CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506"); + CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203"); + CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506"); + + List<CodecSad> expectedList1 = new ArrayList<>(); + expectedList1.add(expectedCodec1); + expectedList1.add(expectedCodec2); + + List<CodecSad> expectedList2 = new ArrayList<>(); + expectedList2.add(expectedCodec3); + expectedList2.add(expectedCodec4); + + DeviceConfig expectedDevice1 = new DeviceConfig( + "VX_AUDIO_DEVICE_IN_HDMI_ARC", expectedList1); + DeviceConfig expectedDevice2 = new DeviceConfig( + "AUDIO_DEVICE_IN_SPDIF", expectedList2); + + List<DeviceConfig> expectedConfig = new ArrayList<>(); + expectedConfig.add(expectedDevice1); + expectedConfig.add(expectedDevice2); + + assertThat(config).isEqualTo(expectedConfig); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java index bd297eecf25c..440a49ab81fc 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java @@ -31,6 +31,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -150,6 +151,11 @@ public class SystemAudioInitiationActionFromAvrTest { int sourceAddress, int physicalAddress) { mBroadcastActiveSource = true; } + + @Override + int pathToPortId(int path) { + return -1; + } }; mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) { @@ -270,6 +276,7 @@ public class SystemAudioInitiationActionFromAvrTest { assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()); } + @Ignore("b/120845532") @Test public void testIsPlaybackDevice_cannotReceiveActiveSource() { resetTestVariables(); @@ -282,10 +289,10 @@ public class SystemAudioInitiationActionFromAvrTest { mTestLooper.dispatchAll(); assertThat(mMsgRequestActiveSourceCount).isEqualTo(1); - assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1); + assertThat(mBroadcastActiveSource).isTrue(); assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1); + assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1); assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue(); - assertThat(mBroadcastActiveSource).isTrue(); } private void resetTestVariables() { diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 1f5c64e92ac0..bc1f7981258d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -42,6 +42,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.IUidObserver; +import android.app.Person; import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -1588,6 +1589,14 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * Make a Person. + */ + protected Person makePerson(CharSequence name, String key, String uri) { + final Person.Builder builder = new Person.Builder(); + return builder.setName(name).setKey(key).setUri(uri).build(); + } + + /** * Make an component name, with the client context. */ @NonNull diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 9b59f9151fd0..8d0365b534b5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -248,6 +248,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) + .setPerson(makePerson("person", "personKey", "personUri")) + .setLongLived() .setExtras(pb) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); @@ -267,9 +269,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); + assertEquals("person", si.getPersons()[0].getName()); + assertEquals("personKey", si.getPersons()[0].getKey()); + assertEquals("personUri", si.getPersons()[0].getUri()); assertEquals(1, si.getExtras().getInt("k")); - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); @@ -345,6 +350,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) + .setPerson(makePerson("person", "personKey", "personUri")) + .setLongLived() .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); @@ -368,9 +375,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); + assertEquals("person", si.getPersons()[0].getName()); + assertEquals("personKey", si.getPersons()[0].getKey()); + assertEquals("personUri", si.getPersons()[0].getUri()); assertEquals(1, si.getExtras().getInt("k")); - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); @@ -388,9 +398,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); + assertEquals("person", si.getPersons()[0].getName()); + assertEquals("personKey", si.getPersons()[0].getKey()); + assertEquals("personUri", si.getPersons()[0].getUri()); assertEquals(1, si.getExtras().getInt("k")); - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); @@ -408,9 +421,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals(null, si.getIntent()); assertEquals(123, si.getRank()); + assertEquals("person", si.getPersons()[0].getName()); + assertEquals("personKey", si.getPersons()[0].getKey()); + assertEquals("personUri", si.getPersons()[0].getUri()); assertEquals(1, si.getExtras().getInt("k")); - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); @@ -428,9 +444,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(null, si.getCategories()); assertEquals(null, si.getIntent()); assertEquals(0, si.getRank()); + assertEquals(null, si.getPersons()); assertEquals(null, si.getExtras()); - assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY + | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); @@ -692,6 +710,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setPerson(makePerson("person", "", "")).build()); + assertEquals("text", si.getText()); + assertEquals("person", si.getPersons()[0].getName()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIntent(makeIntent("action2", ShortcutActivity.class)).build()); assertEquals("text", si.getText()); assertEquals("action2", si.getIntent().getAction()); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 823b7a5ee545..3ebc6ad9b906 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -544,6 +544,18 @@ public class UserManagerTest extends AndroidTestCase { switchUser(-1, UserHandle.of(startUser), false); } + public void testSwitchUserByHandle_ThrowsException() { + synchronized (mUserSwitchLock) { + try { + ActivityManager am = getContext().getSystemService(ActivityManager.class); + am.switchUser(null); + fail("Expected IllegalArgumentException on passing in a null UserHandle."); + } catch (IllegalArgumentException expected) { + // Do nothing - exception is expected. + } + } + } + @MediumTest public void testConcurrentUserCreate() throws Exception { int userCount = mUserManager.getUserCount(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java index 3b6b48b6aa3f..f817e8e33b31 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java @@ -77,7 +77,6 @@ public class DexLoggerTests { @Mock IPackageManager mPM; @Mock Installer mInstaller; - private final Object mInstallLock = new Object(); private PackageDynamicCodeLoading mPackageDynamicCodeLoading; private DexLogger mDexLogger; @@ -103,7 +102,7 @@ public class DexLoggerTests { }; // For test purposes capture log messages as well as sending to the event log. - mDexLogger = new DexLogger(mPM, mInstaller, mInstallLock, mPackageDynamicCodeLoading) { + mDexLogger = new DexLogger(mPM, mInstaller, mPackageDynamicCodeLoading) { @Override void writeDclEvent(int uid, String message) { super.writeDclEvent(uid, message); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 41d569106922..8171469d4da4 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -61,6 +61,7 @@ import android.util.ArraySet; import android.view.Display; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -416,6 +417,7 @@ public class AppStandbyControllerTests { } @Test + @FlakyTest(bugId = 119774928) public void testEnabledState() throws Exception { TestParoleListener paroleListener = new TestParoleListener(); mController.addListener(paroleListener); 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 83c1c7670338..94b21af65799 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3511,9 +3511,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testAppOverlay() throws Exception { - mBinderService.setAppOverlaysAllowed(PKG, mUid, false); - assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid)); + public void testBubble() throws Exception { + mBinderService.setBubblesAllowed(PKG, mUid, false); + assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid)); } @Test 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 0fcfea716e8f..24a1f8c19f12 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -809,7 +809,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); channel.setShowBadge(true); - channel.setAllowAppOverlay(false); + channel.setAllowBubbles(false); int lockMask = 0; for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; @@ -826,7 +826,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(savedChannel.canBypassDnd()); assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); - assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps()); + assertEquals(channel.canBubble(), savedChannel.canBubble()); verify(mHandler, never()).requestSort(); } @@ -840,7 +840,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); channel.setShowBadge(true); - channel.setAllowAppOverlay(false); + channel.setAllowBubbles(false); int lockMask = 0; for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; @@ -857,7 +857,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(savedChannel.canBypassDnd()); assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); - assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps()); + assertEquals(channel.canBubble(), savedChannel.canBubble()); } @Test @@ -969,16 +969,16 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testLockFields_appOverlay() { + public void testLockFields_allowBubble() { mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false); assertEquals(0, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false) .getUserLockedFields()); final NotificationChannel update = getChannel(); - update.setAllowAppOverlay(false); + update.setAllowBubbles(false); mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true); - assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY, + assertEquals(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false) .getUserLockedFields()); } @@ -2161,30 +2161,30 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testAllowAppOverlay_defaults() throws Exception { - assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + public void testAllowBubbles_defaults() throws Exception { + assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); } @Test - public void testAllowAppOverlay_xml() throws Exception { - mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false); - assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); - assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + public void testAllowBubbles_xml() throws Exception { + mHelper.setBubblesAllowed(PKG_O, UID_O, false); + assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); - assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); } @@ -2290,14 +2290,14 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.lockChannelsForOEM(new String[] {PKG_O}); NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE); - update.setAllowAppOverlay(false); + update.setAllowBubbles(false); mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); assertEquals(IMPORTANCE_HIGH, mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); assertEquals(false, - mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canOverlayApps()); + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index 5bf3d2dabe24..56f4a8544f00 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -36,6 +36,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import android.platform.test.annotations.Presubmit; @@ -54,11 +57,6 @@ import org.junit.Test; @Presubmit public class ActivityDisplayTests extends ActivityTestsBase { - @Before - public void setUp() throws Exception { - setupActivityTaskManagerService(); - } - @Test public void testLastFocusedStackIsUpdatedWhenMovingStack() { // Create a stack at bottom. @@ -277,4 +275,60 @@ public class ActivityDisplayTests extends ActivityTestsBase { assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1)); } + + @Test + public void testRemoveStackInWindowingModes() { + removeStackTests(() -> mRootActivityContainer.removeStacksInWindowingModes( + WINDOWING_MODE_FULLSCREEN)); + } + + @Test + public void testRemoveStackWithActivityTypes() { + removeStackTests( + () -> mRootActivityContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD)); + } + + private void removeStackTests(Runnable runnable) { + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack stack1 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack stack2 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack stack3 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack1).setTaskId(1).build(); + final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack2).setTaskId(2).build(); + final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack3).setTaskId(3).build(); + final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack4).setTaskId(4).build(); + + // Reordering stacks while removing stacks. + doAnswer(invocation -> { + display.positionChildAtTop(stack3, false); + return true; + }).when(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(), + any()); + + // Removing stacks from the display while removing stacks. + doAnswer(invocation -> { + display.removeChild(stack2); + return true; + }).when(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(), + any()); + + runnable.run(); + verify(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(), + any()); + verify(mSupervisor).removeTaskByIdLocked(eq(task3.taskId), anyBoolean(), anyBoolean(), + any()); + verify(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(), + any()); + verify(mSupervisor).removeTaskByIdLocked(eq(task1.taskId), anyBoolean(), anyBoolean(), + any()); + } } 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 cac9cf69ce4d..ee228610ab21 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -70,8 +70,6 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { @Before public void setUpAMLO() throws Exception { - setupActivityTaskManagerService(); - mLaunchObserver = mock(ActivityMetricsLaunchObserver.class); // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger. 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 8f9d2badce30..8be63fc43adb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -19,8 +19,10 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.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.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; @@ -39,11 +41,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.app.ActivityOptions; +import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.PauseActivityItem; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.util.MergedConfiguration; import android.util.MutableBoolean; import androidx.test.filters.MediumTest; @@ -67,8 +72,7 @@ public class ActivityRecordTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); - mStack = new StackBuilder(mRootActivityContainer).build(); + mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build(); mTask = mStack.getChildAt(0); mActivity = mTask.getTopActivity(); } @@ -251,4 +255,112 @@ public class ActivityRecordTests extends ActivityTestsBase { assertEquals(prevSeq + 1, appWindowTokenRequestedOrientation.seq); verify(mActivity.mAppWindowToken).onMergedOverrideConfigurationChanged(); } + + @Test + public void testSetsRelaunchReason_NotDragResizing() { + mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); + + mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); + mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + mActivity.getConfiguration())); + + mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(mTask.getConfiguration()); + newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT + ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; + mTask.onRequestedOverrideConfigurationChanged(newConfig); + + mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; + + mActivity.ensureActivityConfiguration(0, false, false); + + assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE, + mActivity.mRelaunchReason); + } + + @Test + public void testSetsRelaunchReason_DragResizing() { + mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); + + mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); + mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + mActivity.getConfiguration())); + + mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(mTask.getConfiguration()); + newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT + ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; + mTask.onRequestedOverrideConfigurationChanged(newConfig); + + doReturn(true).when(mTask.getTask()).isDragResizing(); + + mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; + + mActivity.ensureActivityConfiguration(0, false, false); + + assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE, + mActivity.mRelaunchReason); + } + + @Test + public void testSetsRelaunchReason_NonResizeConfigChanges() { + mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); + + mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); + mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + mActivity.getConfiguration())); + + mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE; + final Configuration newConfig = new Configuration(mTask.getConfiguration()); + newConfig.fontScale = 5; + mTask.onRequestedOverrideConfigurationChanged(newConfig); + + mActivity.mRelaunchReason = + ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; + + mActivity.ensureActivityConfiguration(0, false, false); + + assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE, + mActivity.mRelaunchReason); + } + + @Test + public void testSetRequestedOrientationUpdatesConfiguration() throws Exception { + mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); + + mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); + mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + mActivity.getConfiguration())); + + mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(mActivity.getConfiguration()); + newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT + ? Configuration.ORIENTATION_LANDSCAPE + : Configuration.ORIENTATION_PORTRAIT; + + // Mimic the behavior that display doesn't handle app's requested orientation. + doAnswer(invocation -> { + mTask.onConfigurationChanged(newConfig); + return null; + }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any()); + + final int requestedOrientation; + switch (newConfig.orientation) { + case Configuration.ORIENTATION_LANDSCAPE: + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + break; + case Configuration.ORIENTATION_PORTRAIT: + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + break; + default: + throw new IllegalStateException("Orientation in new config should be either" + + "landscape or portrait."); + } + mActivity.setRequestedOrientation(requestedOrientation); + + final ActivityConfigurationChangeItem expected = + ActivityConfigurationChangeItem.obtain(newConfig); + verify(mService.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()), + eq(mActivity.appToken), eq(expected)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index f7b5d26ac87e..59e71c417439 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -64,7 +64,6 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } 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 b2a28699e6a0..35c1edeace2d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -36,6 +36,8 @@ 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.STOPPING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; +import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; +import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; import static com.google.common.truth.Truth.assertThat; @@ -73,7 +75,6 @@ public class ActivityStackTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); mDefaultDisplay = mRootActivityContainer.getDefaultDisplay(); mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */)); @@ -264,13 +265,13 @@ public class ActivityStackTests extends ActivityTestsBase { // Do not move display to back because there is still another stack. stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask()); - verify(stack2.getWindowContainerController()).positionChildAtBottom(any(), + verify(stack2.getTaskStack()).positionChildAtBottom(any(), eq(false) /* includingParents */); // Also move display to back because there is only one stack left. display.removeChild(stack1); stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask()); - verify(stack2.getWindowContainerController()).positionChildAtBottom(any(), + verify(stack2.getTaskStack()).positionChildAtBottom(any(), eq(true) /* includingParents */); } @@ -687,6 +688,62 @@ public class ActivityStackTests extends ActivityTestsBase { } @Test + public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() { + final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build(); + + activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; + activity.launchCount = 1; + activity.haveState = false; + + mStack.handleAppDiedLocked(activity.app); + + assertEquals(1, mTask.mActivities.size()); + assertEquals(1, mStack.getAllTasks().size()); + } + + @Test + public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() { + final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build(); + + activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; + activity.launchCount = 3; + activity.haveState = false; + + mStack.handleAppDiedLocked(activity.app); + + assertThat(mTask.mActivities).isEmpty(); + assertThat(mStack.getAllTasks()).isEmpty(); + } + + @Test + public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() { + final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build(); + + activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; + activity.launchCount = 1; + activity.haveState = false; + + mStack.handleAppDiedLocked(activity.app); + + assertEquals(1, mTask.mActivities.size()); + assertEquals(1, mStack.getAllTasks().size()); + } + + @Test + public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() { + final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build(); + + activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; + activity.launchCount = 3; + activity.haveState = false; + + mStack.handleAppDiedLocked(activity.app); + + assertThat(mTask.mActivities).isEmpty(); + assertThat(mStack.getAllTasks()).isEmpty(); + } + + @Test public void testFinishCurrentActivity() { // Create 2 activities on a new display. final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java index 2ba2fdbcb959..96db38b14ad5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java @@ -56,7 +56,6 @@ public class ActivityStartControllerTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - mService = createActivityTaskManagerService(); mFactory = mock(Factory.class); mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory); mStarter = spy(new ActivityStarter(mController, mService, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index ec88718dab5d..a381023590c3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -117,7 +117,6 @@ public class ActivityStarterTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); mController = mock(ActivityStartController.class); mActivityMetricsLogger = mock(ActivityMetricsLogger.class); clearInvocations(mActivityMetricsLogger); @@ -143,7 +142,7 @@ public class ActivityStarterTests extends ActivityTestsBase { .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .build(); - assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class); + assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class); mStarter.updateBounds(task2, bounds); verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId), diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 5589ca1be815..68df87e3e27d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -39,9 +39,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doCallRealMethod; - import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -66,11 +63,11 @@ import android.view.Display; import android.view.DisplayInfo; import com.android.internal.app.IVoiceInteractor; -import com.android.server.appop.AppOpsService; import com.android.server.AttributeCache; import com.android.server.ServiceThread; import com.android.server.am.ActivityManagerService; import com.android.server.am.PendingIntentController; +import com.android.server.appop.AppOpsService; import com.android.server.firewall.IntentFirewall; import com.android.server.uri.UriGrantsManagerInternal; @@ -114,6 +111,10 @@ class ActivityTestsBase { @Before public void setUpBase() { mTestInjector.setUp(); + + mService = new TestActivityTaskManagerService(mContext); + mSupervisor = mService.mStackSupervisor; + mRootActivityContainer = mService.mRootActivityContainer; } @After @@ -125,17 +126,6 @@ class ActivityTestsBase { } } - ActivityTaskManagerService createActivityTaskManagerService() { - mService = new TestActivityTaskManagerService(mContext); - mSupervisor = mService.mStackSupervisor; - mRootActivityContainer = mService.mRootActivityContainer; - return mService; - } - - void setupActivityTaskManagerService() { - createActivityTaskManagerService(); - } - /** Creates a {@link TestActivityDisplay}. */ TestActivityDisplay createNewActivityDisplay() { return TestActivityDisplay.create(mSupervisor, sNextDisplayId++); @@ -377,7 +367,7 @@ class ActivityTestsBase { mStack.addTask(task, true, "creating test task"); task.setStack(mStack); task.setTask(); - mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0); + mStack.getTaskStack().addChild(task.mTask, 0); } task.touchActiveTime(); @@ -632,7 +622,7 @@ class ActivityTestsBase { @SuppressWarnings("TypeParameterUnusedInFormals") @Override - <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, + ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop) { return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this) .setWindowingMode(windowingMode).setActivityType(activityType) @@ -681,10 +671,9 @@ class ActivityTestsBase { * method is called. Note that its functionality depends on the implementations of the * construction arguments. */ - protected static class TestActivityStack<T extends StackWindowController> - extends ActivityStack<T> { + protected static class TestActivityStack + extends ActivityStack { private int mOnActivityRemovedFromStackCount = 0; - private T mContainerController; static final int IS_TRANSLUCENT_UNSET = 0; static final int IS_TRANSLUCENT_FALSE = 1; @@ -724,20 +713,20 @@ class ActivityTestsBase { } @Override - protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { - mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController(); + protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) { + mTaskStack = WindowTestUtils.createMockTaskStack(); // Primary pinned stacks require a non-empty out bounds to be set or else all tasks // will be moved to the full screen stack. if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { outBounds.set(0, 0, 100, 100); } - return mContainerController; + } @Override - T getWindowContainerController() { - return mContainerController; + TaskStack getTaskStack() { + return mTaskStack; } void setIsTranslucent(boolean isTranslucent) { @@ -827,27 +816,23 @@ class ActivityTestsBase { } @SuppressWarnings("TypeParameterUnusedInFormals") - <T extends ActivityStack> T build() { + ActivityStack build() { final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId(); if (mWindowingMode == WINDOWING_MODE_PINNED) { - return (T) new PinnedActivityStack(mDisplay, stackId, - mRootActivityContainer.mStackSupervisor, mOnTop) { + return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor, + mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) { @Override Rect getDefaultPictureInPictureBounds(float aspectRatio) { return new Rect(50, 50, 100, 100); } @Override - PinnedStackWindowController createStackWindowController(int displayId, - boolean onTop, Rect outBounds) { - PinnedStackWindowController controller = - mock(PinnedStackWindowController.class); - controller.mContainer = mock(TaskStack.class); - return controller; + void createTaskStack(int displayId, boolean onTop, Rect outBounds) { + mTaskStack = mock(TaskStack.class); } }; } else { - return (T) new TestActivityStack(mDisplay, stackId, + return new TestActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor, mWindowingMode, mActivityType, mOnTop, mCreateActivity); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index fa4228908cea..123de2d227bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -39,7 +39,9 @@ import android.view.Display; import androidx.test.filters.SmallTest; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -52,15 +54,34 @@ import org.junit.Test; @Presubmit public class AppTransitionTests extends WindowTestsBase { + private static RootWindowContainer sOriginalRootWindowContainer; + private DisplayContent mDc; + @BeforeClass + public static void setUpRootWindowContainerMock() { + final WindowManagerService wm = TestSystemServices.getWindowManagerService(); + // For unit test, we don't need to test performSurfacePlacement to prevent some abnormal + // interaction with surfaceflinger native side. + sOriginalRootWindowContainer = wm.mRoot; + // Creating spied mock of RootWindowContainer shouldn't be done in @Before, since it will + // create unnecessary nested spied objects chain, because WindowManagerService object under + // test is a single instance shared among all tests that extend WindowTestsBase class. + // Instead it should be done once before running all tests in this test class. + wm.mRoot = spy(wm.mRoot); + doNothing().when(wm.mRoot).performSurfacePlacement(anyBoolean()); + } + + @AfterClass + public static void tearDownRootWindowContainerMock() { + final WindowManagerService wm = TestSystemServices.getWindowManagerService(); + wm.mRoot = sOriginalRootWindowContainer; + sOriginalRootWindowContainer = null; + } + @Before public void setUp() throws Exception { mDc = mWm.getDefaultDisplayContentLocked(); - // For unit test, we don't need to test performSurfacePlacement to prevent some - // abnormal interaction with surfaceflinger native side. - mWm.mRoot = spy(mWm.mRoot); - doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); } @Test @@ -160,7 +181,7 @@ public class AppTransitionTests extends WindowTestsBase { assertTrue(dc1.mOpeningApps.size() > 0); // Move stack to another display. - stack1.getController().reparent(dc2.getDisplayId(), new Rect(), true); + stack1.reparent(dc2.getDisplayId(), new Rect(), true); // Verify if token are cleared from both pending transition list in former display. assertFalse(dc1.mOpeningApps.contains(token1)); 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 3826fac22b05..f3994630adee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -59,6 +59,7 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; +import android.view.DisplayInfo; import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; @@ -584,15 +585,17 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testOnDescendantOrientationRequestChanged() { - final DisplayContent dc = createNewDisplay(); + final DisplayInfo info = new DisplayInfo(); + info.logicalWidth = 1080; + info.logicalHeight = 1920; + info.logicalDensityDpi = 240; + final DisplayContent dc = createNewDisplay(info); + dc.configureDisplayPolicy(); 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.setOrientation(newOrientation); + window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE; ActivityRecord activityRecord = mock(ActivityRecord.class); @@ -603,21 +606,22 @@ 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_PORTRAIT, newDisplayConfig.orientation); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, newDisplayConfig.orientation); } @Test public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { - final DisplayContent dc = createNewDisplay(); + final DisplayInfo info = new DisplayInfo(); + info.logicalWidth = 1080; + info.logicalHeight = 1920; + info.logicalDensityDpi = 240; + final DisplayContent dc = createNewDisplay(info); 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.setOrientation(newOrientation); + window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE; ActivityRecord activityRecord = mock(ActivityRecord.class); 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 198e7ce63f52..d05711e914c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -33,7 +33,6 @@ 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; @@ -612,23 +611,6 @@ 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/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index bb3ab358e71a..68d8bc078d54 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -94,8 +94,8 @@ public class DragDropControllerTests extends WindowTestsBase { private WindowState createDropTargetWindow(String name, int ownerId) { final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken( mDisplayContent); - final TaskStack stack = createStackControllerOnStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer; + final TaskStack stack = createTaskStackOnDisplay( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); final Task task = createTaskInStack(stack, ownerId); task.addChild(token, 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index 8c3dec7f1e75..b28ae40d5056 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -71,7 +71,6 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - mService = createActivityTaskManagerService(); mPersister = new TestLaunchParamsPersister(); mController = new LaunchParamsController(mService, mPersister); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 86353643c128..aeda473a9e6e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -102,8 +102,6 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { mFolder = new File(cacheFolder, "launch_params_tests"); deleteRecursively(mFolder); - setupActivityTaskManagerService(); - mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++); final DisplayInfo info = new DisplayInfo(); info.uniqueId = mDisplayUniqueId; 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 efd7d25c586d..beaac8e58686 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java @@ -52,7 +52,6 @@ public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mService = createActivityTaskManagerService(); mService.mH.runWithScissors(() -> { mHandler = new TestHandler(null, mClock); }, 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 58302d6b8b75..e3bacf96a86c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -82,7 +82,6 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } @@ -323,8 +322,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { public void testFindTaskToMoveToFrontWhenRecentsOnTop() { // Create stack/task on default display. final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); - final TestActivityStack targetStack = - new StackBuilder(mRootActivityContainer).setOnTop(false).build(); + final TestActivityStack targetStack = (TestActivityStack) new StackBuilder( + mRootActivityContainer).setOnTop(false).build(); final TaskRecord targetTask = targetStack.getChildAt(0); // Create Recents on top of the display. diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index a8b6dc373cbf..dc964806b7a9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -49,7 +49,6 @@ public class RunningTasksTest extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); mRunningTasks = new RunningTasks(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java deleted file mode 100644 index 5690b58737ab..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2017 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.wm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Test class for {@link StackWindowController}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:StackWindowControllerTests - */ -@SmallTest -@Presubmit -public class StackWindowControllerTests extends WindowTestsBase { - @Test - public void testRemoveContainer() { - final StackWindowController stackController = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController); - - final TaskStack stack = stackController.mContainer; - assertNotNull(stack); - assertNotNull(task); - stackController.removeContainer(); - // Assert that the container was removed. - assertNull(stackController.mContainer); - assertNull(stack.getDisplayContent()); - assertNull(task.getDisplayContent()); - assertNull(task.mStack); - } - - @Test - public void testRemoveContainer_deferRemoval() { - final StackWindowController stackController = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController); - - final TaskStack stack = stackController.mContainer; - // Stack removal is deferred if one of its child is animating. - task.setLocalIsAnimating(true); - - stackController.removeContainer(); - // For the case of deferred removal the stack controller will no longer be connected to the - // container, but the task controller will still be connected to the its container until - // the stack window container is removed. - assertNull(stackController.mContainer); - assertNull(stack.getController()); - assertNotNull(task); - - stack.removeImmediately(); - // After removing, the task will be isolated. - assertNull(task.getParent()); - assertEquals(task.getChildCount(), 0); - assertNull(task.getController()); - } - - @Test - public void testReparent() { - // Create first stack on primary display. - final StackWindowController stack1Controller = - createStackControllerOnDisplay(mDisplayContent); - final TaskStack stack1 = stack1Controller.mContainer; - final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1Controller); - task1.mOnDisplayChangedCalled = false; - - // Create second display and put second stack on it. - final DisplayContent dc = createNewDisplay(); - final StackWindowController stack2Controller = - createStackControllerOnDisplay(dc); - final TaskStack stack2 = stack2Controller.mContainer; - - // Reparent - stack1Controller.reparent(dc.getDisplayId(), new Rect(), true /* onTop */); - assertEquals(dc, stack1.getDisplayContent()); - final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); - final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); - assertEquals(stack1PositionInParent, stack2PositionInParent + 1); - assertTrue(task1.mOnDisplayChangedCalled); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 53e99fae95d4..ace179acdeb5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -75,10 +75,6 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); - mService.mSupportsFreeformWindowManagement = true; - when(mSupervisor.canUseActivityOptionsLaunchBounds(any())).thenCallRealMethod(); - mActivity = new ActivityBuilder(mService).build(); mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1; mActivity.info.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; 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 8a98cbe5cd52..e182c45f009e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -25,6 +25,13 @@ 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; @@ -46,6 +53,7 @@ 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; @@ -82,7 +90,6 @@ public class TaskRecordTests extends ActivityTestsBase { @Before public void setUp() throws Exception { TaskRecord.setTaskRecordFactory(null); - setupActivityTaskManagerService(); mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); } @@ -217,12 +224,17 @@ public class TaskRecordTests extends ActivityTestsBase { info.logicalHeight = fullScreenBounds.height(); ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP); assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null); + // Override display orientation. Normally this is available via DisplayContent, but DC + // is mocked-out. + display.getRequestedOverrideConfiguration().orientation = + Configuration.ORIENTATION_LANDSCAPE; + display.onRequestedOverrideConfigurationChanged( + display.getRequestedOverrideConfiguration()); ActivityStack stack = new StackBuilder(mRootActivityContainer) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); TaskRecord task = stack.getChildAt(0); - ActivityRecord root = task.getRootActivity(); - ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build(); - assertEquals(root, task.getRootActivity()); + ActivityRecord root = task.getTopActivity(); + assertEquals(root, task.getTopActivity()); assertEquals(fullScreenBounds, task.getBounds()); @@ -233,16 +245,22 @@ public class TaskRecordTests extends ActivityTestsBase { assertTrue(task.getBounds().width() < task.getBounds().height()); assertEquals(fullScreenBounds.height(), task.getBounds().height()); - // Setting non-root app has no effect - setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE); - assertTrue(task.getBounds().width() < task.getBounds().height()); + // Top activity gets used + ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build(); + assertEquals(top, task.getTopActivity()); + setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE); + assertTrue(task.getBounds().width() > task.getBounds().height()); + assertEquals(task.getBounds().width(), fullScreenBounds.width()); // Setting app to unspecified restores - setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED); + setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED); assertEquals(fullScreenBounds, task.getBounds()); // Setting app to fixed landscape and changing display - setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE); + setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE); + // simulate display orientation changing (normally done via DisplayContent) + display.getRequestedOverrideConfiguration().orientation = + Configuration.ORIENTATION_PORTRAIT; display.setBounds(fullScreenBoundsPort); assertTrue(task.getBounds().width() > task.getBounds().height()); assertEquals(fullScreenBoundsPort.width(), task.getBounds().width()); @@ -265,6 +283,51 @@ public class TaskRecordTests extends ActivityTestsBase { assertEquals(freeformBounds, task.getBounds()); } + @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()); + + final 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))); + } + /** Ensures that the alias intent won't have target component resolved. */ @Test public void testTaskIntentActivityAlias() { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java index f01e9f0662c9..74ccccca43bb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java @@ -47,8 +47,8 @@ public class TaskStackContainersTests extends WindowTestsBase { @Before public void setUp() throws Exception { - mPinnedStack = createStackControllerOnStackOnDisplay( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer; + mPinnedStack = createTaskStackOnDisplay( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent); // Stack should contain visible app window to be considered visible. final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); assertFalse(mPinnedStack.isVisible()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 7ac331829fb1..2554237cdbf5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -20,8 +20,12 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -106,4 +110,62 @@ public class TaskStackTests extends WindowTestsBase { assertNull(stack.getDisplayContent()); assertNull(task.mStack); } + + @Test + public void testRemoveContainer() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack); + + assertNotNull(stack); + assertNotNull(task); + stack.removeIfPossible(); + // Assert that the container was removed. + assertNull(stack.getParent()); + assertEquals(0, stack.getChildCount()); + assertNull(stack.getDisplayContent()); + assertNull(task.getDisplayContent()); + assertNull(task.mStack); + } + + @Test + public void testRemoveContainer_deferRemoval() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack); + + // Stack removal is deferred if one of its child is animating. + task.setLocalIsAnimating(true); + + stack.removeIfPossible(); + // For the case of deferred removal the task controller will still be connected to the its + // container until the stack window container is removed. + assertNotNull(stack.getParent()); + assertNotEquals(0, stack.getChildCount()); + assertNotNull(task); + + stack.removeImmediately(); + // After removing, the task will be isolated. + assertNull(task.getParent()); + assertEquals(0, task.getChildCount()); + assertNull(task.getController()); + } + + @Test + public void testReparent() { + // Create first stack on primary display. + final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); + final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1); + task1.mOnDisplayChangedCalled = false; + + // Create second display and put second stack on it. + final DisplayContent dc = createNewDisplay(); + final TaskStack stack2 = createTaskStackOnDisplay(dc); + + // Reparent + stack1.reparent(dc.getDisplayId(), new Rect(), true /* onTop */); + assertEquals(dc, stack1.getDisplayContent()); + final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); + final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); + assertEquals(stack1PositionInParent, stack2PositionInParent + 1); + assertTrue(task1.mOnDisplayChangedCalled); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 3e32ed37a6f3..ae211d3f1e38 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -40,8 +40,7 @@ public class TaskTests extends WindowTestsBase { @Test public void testRemoveContainer() { - final StackWindowController stackController1 = - createStackControllerOnDisplay(mDisplayContent); + final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent); final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1); final WindowTestUtils.TestAppWindowToken appToken = WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task); @@ -49,14 +48,13 @@ public class TaskTests extends WindowTestsBase { task.removeIfPossible(); // Assert that the container was removed. assertNull(task.getParent()); - assertEquals(task.getChildCount(), 0); + assertEquals(0, task.getChildCount()); assertNull(appToken.getParent()); } @Test public void testRemoveContainer_deferRemoval() { - final StackWindowController stackController1 = - createStackControllerOnDisplay(mDisplayContent); + final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent); final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1); final WindowTestUtils.TestAppWindowToken appToken = WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task); @@ -67,22 +65,20 @@ public class TaskTests extends WindowTestsBase { // For the case of deferred removal the task will still be connected to the its app token // until the task window container is removed. assertNotNull(task.getParent()); - assertNotEquals(task.getChildCount(), 0); + assertNotEquals(0, task.getChildCount()); assertNotNull(appToken.getParent()); task.removeImmediately(); assertNull(task.getParent()); - assertEquals(task.getChildCount(), 0); + assertEquals(0, task.getChildCount()); assertNull(appToken.getParent()); } @Test public void testReparent() { - final StackWindowController stackController1 = - createStackControllerOnDisplay(mDisplayContent); + final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent); final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1); - final StackWindowController stackController2 = - createStackControllerOnDisplay(mDisplayContent); + final TaskStack stackController2 = createTaskStackOnDisplay(mDisplayContent); final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2); boolean gotException = false; @@ -93,20 +89,17 @@ public class TaskTests extends WindowTestsBase { } assertTrue("Should not be able to reparent to the same parent", gotException); - final StackWindowController stackController3 = - createStackControllerOnDisplay(mDisplayContent); - stackController3.setContainer(null); gotException = false; try { - task.reparent(stackController3, 0, false/* moveParents */); + task.reparent(null, 0, false/* moveParents */); } catch (IllegalArgumentException e) { gotException = true; } - assertTrue("Should not be able to reparent to a stack that doesn't have a container", + assertTrue("Should not be able to reparent to a stack that doesn't exist", gotException); task.reparent(stackController2, 0, false/* moveParents */); - assertEquals(stackController2.mContainer, task.getParent()); + assertEquals(stackController2, task.getParent()); assertEquals(0, task.positionInParent()); assertEquals(1, task2.positionInParent()); } @@ -114,20 +107,17 @@ public class TaskTests extends WindowTestsBase { @Test public void testReparent_BetweenDisplays() { // Create first stack on primary display. - final StackWindowController stack1Controller = - createStackControllerOnDisplay(mDisplayContent); - final TaskStack stack1 = stack1Controller.mContainer; - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1Controller); + final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); + final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1); task.mOnDisplayChangedCalled = false; assertEquals(mDisplayContent, stack1.getDisplayContent()); // Create second display and put second stack on it. final DisplayContent dc = createNewDisplay(); - final StackWindowController stack2Controller = createStackControllerOnDisplay(dc); - final TaskStack stack2 = stack2Controller.mContainer; - final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2Controller); + final TaskStack stack2 = createTaskStackOnDisplay(dc); + final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2); // Reparent and check state - task.reparent(stack2Controller, 0, false /* moveParents */); + task.reparent(stack2, 0, false /* moveParents */); assertEquals(stack2, task.getParent()); assertEquals(0, task.positionInParent()); assertEquals(1, task2.positionInParent()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java index 05ac8c1072c0..b151fb7a613c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java @@ -66,7 +66,7 @@ import java.util.concurrent.CountDownLatch; /** * A Test utility class to create a mock {@link WindowManagerService} instance for tests. */ -class WmServiceUtils { +class TestSystemServices { private static StaticMockitoSession sMockitoSession; private static WindowManagerService sService; private static TestWindowManagerPolicy sPolicy; @@ -78,7 +78,7 @@ class WmServiceUtils { .strictness(Strictness.LENIENT) .startMocking(); - runWithDexmakerShareClassLoader(WmServiceUtils::setUpTestWindowService); + runWithDexmakerShareClassLoader(TestSystemServices::setUpTestWindowService); } static void tearDownWindowManagerService() { @@ -147,7 +147,7 @@ class WmServiceUtils { final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock(); doReturn(wmLock).when(atms).getGlobalLock(); - sPolicy = new TestWindowManagerPolicy(WmServiceUtils::getWindowManagerService); + sPolicy = new TestWindowManagerPolicy(TestSystemServices::getWindowManagerService); sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms); sService.onInitReady(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index d9ef10d7eecb..0a4a8a43fbc5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -16,12 +16,15 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; import static android.view.WindowManager.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; import android.app.ActivityManager.TaskDescription; import android.content.res.Configuration; @@ -58,7 +61,7 @@ public class WindowFrameTests extends WindowTestsBase { boolean mDockedResizingForTest = false; WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken, WindowManager.LayoutParams attrs, Task t) { - super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0, + super(wm, mock(Session.class), iWindow, windowToken, null, 0, 0, attrs, 0, 0, false /* ownerCanAddInternalSystemWindow */); mTask = t; } @@ -113,9 +116,9 @@ public class WindowFrameTests extends WindowTestsBase { @Before public void setUp() throws Exception { - mWindowToken = WindowTestUtils.createTestAppWindowToken( - mWm.getDefaultDisplayContentLocked()); - mStubStack = new TaskStack(mWm, 0, null); + mWindowToken = createAppWindowToken(mWm.getDefaultDisplayContentLocked(), + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + mStubStack = WindowTestUtils.createMockTaskStack(); } // Do not use this function directly in the tests below. Instead, use more explicit function diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index 20fc5ae2454e..44e998b7e62a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -103,12 +103,10 @@ public class WindowTestUtils { } /** - * Creates a mock instance of {@link StackWindowController}. + * Creates a mock instance of {@link TestTaskStack}. */ - public static StackWindowController createMockStackWindowContainerController() { - StackWindowController controller = mock(StackWindowController.class); - controller.mContainer = mock(TestTaskStack.class); - return controller; + public static TaskStack createMockTaskStack() { + return mock(TestTaskStack.class); } /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ @@ -201,6 +199,11 @@ public class WindowTestUtils { } @Override + void onParentSet() { + // Do nothing. + } + + @Override boolean isOnTop() { return mOnTop; } @@ -283,10 +286,9 @@ public class WindowTestUtils { } } - public static TestTask createTestTask(StackWindowController stackWindowController) { - return new TestTask(sNextTaskId++, stackWindowController.mContainer, 0, - stackWindowController.mService, RESIZE_MODE_UNRESIZEABLE, false, - mock(TaskRecord.class)); + public static TestTask createTestTask(TaskStack stack) { + return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE, + false, mock(TaskRecord.class)); } public static class TestIApplicationToken implements IApplicationToken { @@ -334,5 +336,10 @@ public class WindowTestUtils { mHasSurface = hadSurface; } + + @Override + void onParentSet() { + // Do nothing; + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index cdc0a477b9c6..e38118e875e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -41,7 +41,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import android.content.Context; import android.content.res.Configuration; -import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; import android.testing.DexmakerShareClassLoaderRule; import android.util.Log; @@ -102,14 +101,14 @@ class WindowTestsBase { public static void setUpOnceBase() { AttributeCache.init(getInstrumentation().getTargetContext()); - WmServiceUtils.setUpWindowManagerService(); + TestSystemServices.setUpWindowManagerService(); sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); } @AfterClass public static void tearDonwOnceBase() { - WmServiceUtils.tearDownWindowManagerService(); + TestSystemServices.tearDownWindowManagerService(); } @Before @@ -121,7 +120,7 @@ class WindowTestsBase { final Context context = getInstrumentation().getTargetContext(); - mWm = WmServiceUtils.getWindowManagerService(); + mWm = TestSystemServices.getWindowManagerService(); beforeCreateDisplay(); context.getDisplay().getDisplayInfo(mDisplayInfo); @@ -199,7 +198,7 @@ class WindowTestsBase { } // Cleaned up everything in Handler. - WmServiceUtils.cleanupWindowManagerHandlers(); + TestSystemServices.cleanupWindowManagerHandlers(); } catch (Exception e) { Log.e(TAG, "Failed to tear down test", e); throw e; @@ -220,7 +219,7 @@ class WindowTestsBase { * Waits until the main handler for WM has processed all messages. */ void waitUntilHandlersIdle() { - WmServiceUtils.waitUntilWindowManagerHandlersIdle(); + TestSystemServices.waitUntilWindowManagerHandlersIdle(); } private WindowToken createWindowToken( @@ -240,8 +239,7 @@ class WindowTestsBase { WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int windowingMode, int activityType) { - final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType, - dc).mContainer; + final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc); final Task task = createTaskInStack(stack, 0 /* userId */); final WindowTestUtils.TestAppWindowToken appWindowToken = WindowTestUtils.createTestAppWindowToken(dc); @@ -350,28 +348,20 @@ class WindowTestsBase { /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */ TaskStack createTaskStackOnDisplay(DisplayContent dc) { synchronized (mWm.mGlobalLock) { - return createStackControllerOnDisplay(dc).mContainer; + return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); } } - StackWindowController createStackControllerOnDisplay(DisplayContent dc) { - synchronized (mWm.mGlobalLock) { - return createStackControllerOnStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); - } - } - - StackWindowController createStackControllerOnStackOnDisplay( - int windowingMode, int activityType, DisplayContent dc) { + TaskStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) { synchronized (mWm.mGlobalLock) { final Configuration overrideConfig = new Configuration(); overrideConfig.windowConfiguration.setWindowingMode(windowingMode); overrideConfig.windowConfiguration.setActivityType(activityType); final int stackId = ++sNextStackId; - final StackWindowController controller = new StackWindowController(stackId, null, - dc.getDisplayId(), true /* onTop */, new Rect(), mWm); - controller.onRequestedOverrideConfigurationChanged(overrideConfig); - return controller; + final TaskStack stack = new TaskStack(mWm, stackId, mock(ActivityStack.class)); + dc.setStackOnDisplay(stackId, true, stack); + stack.onRequestedOverrideConfigurationChanged(overrideConfig); + return stack; } } diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 94cc6502a12d..9a5bd1379717 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -22,6 +22,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE; import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; @@ -66,7 +67,7 @@ public class IntervalStats { public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>(); public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>(); public Configuration activeConfiguration; - public EventList events; + public EventList events = new EventList(); // A string cache. This is important as when we're parsing XML files, we don't want to // keep hundreds of strings that have the same contents. We will read the string @@ -112,6 +113,9 @@ public class IntervalStats { } + public IntervalStats() { + } + /** * Gets the UsageStats object for the given package, or creates one and adds it internally. */ @@ -253,6 +257,7 @@ public class IntervalStats { case ROLLOVER_FOREGROUND_SERVICE: case CONTINUE_PREVIOUS_DAY: case CONTINUING_FOREGROUND_SERVICE: + case DEVICE_SHUTDOWN: return true; } return false; @@ -281,8 +286,9 @@ public class IntervalStats { @VisibleForTesting public void update(String packageName, String className, long timeStamp, int eventType, int instanceId) { - if (eventType == FLUSH_TO_DISK) { - // FLUSH_TO_DISK are sent to all packages. + if (eventType == DEVICE_SHUTDOWN + || eventType == FLUSH_TO_DISK) { + // DEVICE_SHUTDOWN and FLUSH_TO_DISK are sent to all packages. final int size = packageStats.size(); for (int i = 0; i < size; i++) { UsageStats usageStats = packageStats.valueAt(i); @@ -321,9 +327,6 @@ public class IntervalStats { */ @VisibleForTesting public void addEvent(Event event) { - if (events == null) { - events = new EventList(); - } // Cache common use strings event.mPackage = getCachedStringRef(event.mPackage); if (event.mClass != null) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index f146370b01d7..76a3aa81530e 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION; import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION; import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION; @@ -416,11 +417,30 @@ public class UsageStatsService extends SystemService implements */ void shutdown() { synchronized (mLock) { + mHandler.removeMessages(MSG_REPORT_EVENT); + Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime()); + // orderly shutdown, the last event is DEVICE_SHUTDOWN. + reportEventToAllUserId(event); flushToDiskLocked(); } } /** + * After power button is pressed for 3.5 seconds + * (as defined in {@link com.android.internal.R.integer#config_veryLongPressTimeout}), + * report DEVICE_SHUTDOWN event and persist the database. If the power button is pressed for 10 + * seconds and the device is shutdown, the database is already persisted and we are not losing + * data. + * This method is called from PhoneWindowManager, do not synchronize on mLock otherwise + * PhoneWindowManager may be blocked. + */ + void prepareForPossibleShutdown() { + Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime()); + mHandler.obtainMessage(MSG_REPORT_EVENT_TO_ALL_USERID, event).sendToTarget(); + mHandler.sendEmptyMessage(MSG_FLUSH_TO_DISK); + } + + /** * Called by the Binder stub. */ void reportEvent(Event event, int userId) { @@ -1487,6 +1507,11 @@ public class UsageStatsService extends SystemService implements } @Override + public void prepareForPossibleShutdown() { + UsageStatsService.this.prepareForPossibleShutdown(); + } + + @Override public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) { mAppStandby.addListener(listener); listener.onParoleStateChanged(isAppIdleParoleOn()); diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 5128ae1f9130..2d1098c7cf5c 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -16,6 +16,7 @@ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageStatsManager.INTERVAL_BEST; import static android.app.usage.UsageStatsManager.INTERVAL_COUNT; import static android.app.usage.UsageStatsManager.INTERVAL_DAILY; @@ -134,6 +135,18 @@ class UserUsageStatsService { updateRolloverDeadline(); } + // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp + // is last time UsageStatsDatabase is persisted to disk. + final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY]; + if (currentDailyStats != null) { + final int size = currentDailyStats.events.size(); + if (size == 0 || currentDailyStats.events.get(size - 1).mEventType != DEVICE_SHUTDOWN) { + // The last event in event list is not DEVICE_SHUTDOWN, then we insert one. + final Event event = new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved); + currentDailyStats.addEvent(event); + } + } + if (mDatabase.isNewUpdate()) { notifyNewUpdate(); } @@ -175,7 +188,9 @@ class UserUsageStatsService { // ACTIVITY_STOPPED. && event.mEventType != Event.ACTIVITY_DESTROYED // FLUSH_TO_DISK is a private event. - && event.mEventType != Event.FLUSH_TO_DISK) { + && event.mEventType != Event.FLUSH_TO_DISK + // DEVICE_SHUTDOWN is added to event list after reboot. + && event.mEventType != Event.DEVICE_SHUTDOWN) { currentDailyStats.addEvent(event); } @@ -393,10 +408,6 @@ class UserUsageStatsService { @Override public void combine(IntervalStats stats, boolean mutable, List<Event> accumulatedResult) { - if (stats.events == null) { - return; - } - final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); for (int i = startIndex; i < size; i++) { @@ -434,10 +445,6 @@ class UserUsageStatsService { names.add(packageName); final List<Event> results = queryStats(INTERVAL_DAILY, beginTime, endTime, (stats, mutable, accumulatedResult) -> { - if (stats.events == null) { - return; - } - final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); for (int i = startIndex; i < size; i++) { @@ -696,10 +703,6 @@ class UserUsageStatsService { @Override public void combine(IntervalStats stats, boolean mutable, List<Event> accumulatedResult) { - if (stats.events == null) { - return; - } - final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); for (int i = startIndex; i < size; i++) { @@ -925,10 +928,12 @@ class UserUsageStatsService { return "SCREEN_INTERACTIVE"; case Event.SCREEN_NON_INTERACTIVE: return "SCREEN_NON_INTERACTIVE"; - case UsageEvents.Event.KEYGUARD_SHOWN: + case Event.KEYGUARD_SHOWN: return "KEYGUARD_SHOWN"; - case UsageEvents.Event.KEYGUARD_HIDDEN: + case Event.KEYGUARD_HIDDEN: return "KEYGUARD_HIDDEN"; + case Event.DEVICE_SHUTDOWN: + return "DEVICE_SHUTDOWN"; default: return "UNKNOWN_TYPE_" + eventType; } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index bbf3d45d7c99..613c4ffceffc 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -371,14 +371,15 @@ public class VoiceInteractionManagerService extends SystemService { } private boolean shouldEnableService(Context context) { - // VoiceInteractionService should not be enabled on any low RAM devices - // or devices that have not declared the recognition feature, unless the - // device's configuration has explicitly set the config flag for a fixed + // VoiceInteractionService should not be enabled on devices that have not declared the + // recognition feature (including low-ram devices where notLowRam="true" takes effect), + // unless the device's configuration has explicitly set the config flag for a fixed // voice interaction service. - return (!ActivityManager.isLowRamDeviceStatic() - && context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_VOICE_RECOGNIZERS)) || - getForceVoiceInteractionServicePackage(context.getResources()) != null; + if (getForceVoiceInteractionServicePackage(context.getResources()) != null) { + return true; + } + return context.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS); } private String getForceVoiceInteractionServicePackage(Resources res) { diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc index e95041ba34a4..09cdbd5fee58 100644 --- a/startop/view_compiler/apk_layout_compiler.cc +++ b/startop/view_compiler/apk_layout_compiler.cc @@ -79,9 +79,9 @@ bool CanCompileLayout(ResXMLParser* parser) { return visitor.can_compile(); } -void CompileApkLayouts(const std::string& filename, CompilationTarget target, - std::ostream& target_out) { - auto assets = android::ApkAssets::Load(filename); +namespace { +void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets, + CompilationTarget target, std::ostream& target_out) { android::AssetManager2 resources; resources.SetApkAssets({assets.get()}); @@ -155,5 +155,20 @@ void CompileApkLayouts(const std::string& filename, CompilationTarget target, target_out.write(image.ptr<const char>(), image.size()); } } +} // namespace + +void CompileApkLayouts(const std::string& filename, CompilationTarget target, + std::ostream& target_out) { + auto assets = android::ApkAssets::Load(filename); + CompileApkAssetsLayouts(assets, target, target_out); +} + +void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target, + std::ostream& target_out) { + constexpr const char* friendly_name{"viewcompiler assets"}; + auto assets = android::ApkAssets::LoadFromFd( + std::move(fd), friendly_name, /*system=*/false, /*force_shared_lib=*/false); + CompileApkAssetsLayouts(assets, target, target_out); +} } // namespace startop diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h index c85ddd65ac1b..03bd545d9121 100644 --- a/startop/view_compiler/apk_layout_compiler.h +++ b/startop/view_compiler/apk_layout_compiler.h @@ -19,12 +19,16 @@ #include <string> +#include "android-base/unique_fd.h" + namespace startop { enum class CompilationTarget { kJavaLanguage, kDex }; void CompileApkLayouts(const std::string& filename, CompilationTarget target, std::ostream& target_out); +void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target, + std::ostream& target_out); } // namespace startop diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc index 871a421cee2d..11ecde27f5cd 100644 --- a/startop/view_compiler/main.cc +++ b/startop/view_compiler/main.cc @@ -49,6 +49,7 @@ constexpr char kStdoutFilename[]{"stdout"}; DEFINE_bool(apk, false, "Compile layouts in an APK"); DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); +DEFINE_int32(infd, -1, "Read input from the given file descriptor"); DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); DEFINE_string(package, "", "The package name for the generated class (required)"); @@ -95,7 +96,7 @@ void CompileLayout(XMLDocument* xml, Builder* builder) { int main(int argc, char** argv) { constexpr size_t kProgramName = 0; constexpr size_t kFileNameParam = 1; - constexpr size_t kNumRequiredArgs = 2; + constexpr size_t kNumRequiredArgs = 1; gflags::SetUsageMessage( "Compile XML layout files into equivalent Java language code\n" @@ -104,12 +105,11 @@ int main(int argc, char** argv) { gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); - if (argc != kNumRequiredArgs || cmd.is_default) { + if (argc < kNumRequiredArgs || cmd.is_default) { gflags::ShowUsageWithFlags(argv[kProgramName]); return 1; } - const char* const filename = argv[kFileNameParam]; const bool is_stdout = FLAGS_out == kStdoutFilename; std::ofstream outfile; @@ -118,13 +118,23 @@ int main(int argc, char** argv) { } if (FLAGS_apk) { - startop::CompileApkLayouts( - filename, - FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage, - is_stdout ? std::cout : outfile); + const startop::CompilationTarget target = + FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage; + if (FLAGS_infd >= 0) { + startop::CompileApkLayoutsFd( + android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile); + } else { + if (argc < 2) { + gflags::ShowUsageWithFlags(argv[kProgramName]); + return 1; + } + const char* const filename = argv[kFileNameParam]; + startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile); + } return 0; } + const char* const filename = argv[kFileNameParam]; const string layout_name = startop::util::FindLayoutNameFromFilename(filename); XMLDocument xml; diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java index fe07370394ad..4da79b34a55e 100644 --- a/telephony/java/android/telephony/AvailableNetworkInfo.java +++ b/telephony/java/android/telephony/AvailableNetworkInfo.java @@ -110,6 +110,7 @@ public final class AvailableNetworkInfo implements Parcelable { private AvailableNetworkInfo(Parcel in) { mSubId = in.readInt(); mPriority = in.readInt(); + mMccMncs = new ArrayList<>(); in.readStringList(mMccMncs); } diff --git a/telephony/java/android/telephony/CellConfigLte.java b/telephony/java/android/telephony/CellConfigLte.java index 35769f04c5ae..eafbfbc5076d 100644 --- a/telephony/java/android/telephony/CellConfigLte.java +++ b/telephony/java/android/telephony/CellConfigLte.java @@ -34,6 +34,11 @@ public class CellConfigLte implements Parcelable { } /** @hide */ + public CellConfigLte(android.hardware.radio.V1_4.CellConfigLte cellConfig) { + mIsEndcAvailable = cellConfig.isEndcAvailable; + } + + /** @hide */ public CellConfigLte(boolean isEndcAvailable) { mIsEndcAvailable = isEndcAvailable; } diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java index b761bd7bf70c..8ce5c54c810e 100644 --- a/telephony/java/android/telephony/CellInfo.java +++ b/telephony/java/android/telephony/CellInfo.java @@ -19,8 +19,10 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; +import android.hardware.radio.V1_4.CellInfo.Info; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import com.android.internal.annotations.VisibleForTesting; @@ -318,6 +320,13 @@ public abstract class CellInfo implements Parcelable { } /** @hide */ + protected CellInfo(android.hardware.radio.V1_4.CellInfo ci) { + this.mRegistered = ci.isRegistered; + this.mTimeStamp = SystemClock.elapsedRealtimeNanos(); + this.mCellConnectionStatus = ci.connectionStatus; + } + + /** @hide */ public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) { if (ci == null) return null; switch(ci.cellInfoType) { @@ -342,4 +351,17 @@ public abstract class CellInfo implements Parcelable { default: return null; } } + + /** @hide */ + public static CellInfo create(android.hardware.radio.V1_4.CellInfo ci) { + if (ci == null) return null; + switch (ci.info.getDiscriminator()) { + case Info.hidl_discriminator.gsm: return new CellInfoGsm(ci); + case Info.hidl_discriminator.cdma: return new CellInfoCdma(ci); + case Info.hidl_discriminator.lte: return new CellInfoLte(ci); + case Info.hidl_discriminator.wcdma: return new CellInfoWcdma(ci); + case Info.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci); + default: return null; + } + } } diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index c9f07dab6bbd..4440108879f2 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -67,6 +67,15 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); } + /** @hide */ + public CellInfoCdma(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoCdma cic = ci.info.cdma(); + mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma); + mCellSignalStrengthCdma = + new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); + } + @Override public CellIdentityCdma getCellIdentity() { return mCellIdentityCdma; diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index ad16dfae7295..248adfcb2d90 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -63,6 +63,14 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); } + /** @hide */ + public CellInfoGsm(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoGsm cig = ci.info.gsm(); + mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm); + mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); + } + @Override public CellIdentityGsm getCellIdentity() { return mCellIdentityGsm; diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 75938317bf44..8e8ce8a1f5de 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -70,6 +70,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellConfig = new CellConfigLte(); } + /** @hide */ + public CellInfoLte(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_4.CellInfoLte cil = ci.info.lte(); + mCellIdentityLte = new CellIdentityLte(cil.base.cellIdentityLte); + mCellSignalStrengthLte = new CellSignalStrengthLte(cil.base.signalStrengthLte); + mCellConfig = new CellConfigLte(cil.cellConfig); + } + @Override public CellIdentityLte getCellIdentity() { if (DBG) log("getCellIdentity: " + mCellIdentityLte); diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index a8c49b7bf68e..2ab38fb77a90 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -64,6 +64,14 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); } + /** @hide */ + public CellInfoTdscdma(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoTdscdma cit = ci.info.tdscdma(); + mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma); + mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); + } + @Override public CellIdentityTdscdma getCellIdentity() { return mCellIdentityTdscdma; } diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index a427e80fd65c..65e047077639 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -63,6 +63,14 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable { mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); } + /** @hide */ + public CellInfoWcdma(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoWcdma ciw = ci.info.wcdma(); + mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma); + mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); + } + @Override public CellIdentityWcdma getCellIdentity() { return mCellIdentityWcdma; diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index c81670139eae..9fee5932dadc 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -805,9 +805,11 @@ public class PhoneStateListener { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; - Binder.withCleanCallingIdentity( - () -> mExecutor.execute( - () -> psl.onDataConnectionStateChanged(state, networkType))); + Binder.withCleanCallingIdentity(() -> mExecutor.execute( + () -> { + psl.onDataConnectionStateChanged(state, networkType); + psl.onDataConnectionStateChanged(state); + })); } public void onDataActivity(int direction) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index babeb7b1b61c..3311218bdb3c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9963,7 +9963,7 @@ public class TelephonyManager { boolean ret = false; try { IOns iOpportunisticNetworkService = getIOns(); - if (iOpportunisticNetworkService != null) { + if (iOpportunisticNetworkService != null && availableNetworks != null) { ret = iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks, pkgForDebug); } diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index a5f56bbebd75..e68256d086bc 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -27,6 +27,7 @@ import android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -176,6 +177,12 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu * Bit-field which indicates the number is from the platform-maintained database. */ public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 1 << 4; + /** + * Bit-field which indicates the number is from test mode. + * + * @hide + */ + public static final int EMERGENCY_NUMBER_SOURCE_TEST = 1 << 5; /** Bit-field which indicates the number is from the modem config. */ public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = EmergencyNumberSource.MODEM_CONFIG; @@ -327,6 +334,21 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu } /** + * Returns the bitmask of emergency service categories of the emergency number for + * internal dialing. + * + * @return bitmask of the emergency service categories + * + * @hide + */ + public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmaskInternalDial() { + if (mEmergencyNumberSourceBitmask == EMERGENCY_NUMBER_SOURCE_DATABASE) { + return EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED; + } + return mEmergencyServiceCategoryBitmask; + } + + /** * Returns the emergency service categories of the emergency number. * * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only @@ -577,6 +599,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu emergencyNumberList.remove(i--); } } + Collections.sort(emergencyNumberList); } /** @@ -613,6 +636,12 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) { return false; } + // Never merge two numbers if one of them is from test mode but the other one is not; + // This supports to remove a number from the test mode. + if (first.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST) + ^ second.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)) { + return false; + } return true; } @@ -638,4 +667,13 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu } return null; } + + /** + * Validate Emergency Number address that only allows '0'-'9', '*', or '#' + * + * @hide + */ + public static boolean validateEmergencyNumberAddress(String address) { + return address.matches("[0-9*#]+"); + } } diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 525a96a4dae9..59167b7d5bba 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -347,6 +347,9 @@ public final class ImsCallProfile implements Parcelable { private @EmergencyCallRouting int mEmergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; + /** Indicates if the call is for testing purpose */ + private boolean mEmergencyCallTesting = false; + /** * Extras associated with this {@link ImsCallProfile}. * <p> @@ -534,9 +537,10 @@ public final class ImsCallProfile implements Parcelable { + ", callType=" + mCallType + ", restrictCause=" + mRestrictCause + ", mediaProfile=" + mMediaProfile.toString() - + ", emergencyServiceCategories=" + mEmergencyCallRouting + + ", emergencyServiceCategories=" + mEmergencyServiceCategories + ", emergencyUrns=" + mEmergencyUrns - + ", emergencyCallRouting=" + mEmergencyCallRouting + " }"; + + ", emergencyCallRouting=" + mEmergencyCallRouting + + ", emergencyCallTesting=" + mEmergencyCallTesting + " }"; } @Override @@ -554,6 +558,7 @@ public final class ImsCallProfile implements Parcelable { out.writeInt(mEmergencyServiceCategories); out.writeStringList(mEmergencyUrns); out.writeInt(mEmergencyCallRouting); + out.writeBoolean(mEmergencyCallTesting); } private void readFromParcel(Parcel in) { @@ -564,6 +569,7 @@ public final class ImsCallProfile implements Parcelable { mEmergencyServiceCategories = in.readInt(); mEmergencyUrns = in.createStringArrayList(); mEmergencyCallRouting = in.readInt(); + mEmergencyCallTesting = in.readBoolean(); } public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() { @@ -784,9 +790,11 @@ public final class ImsCallProfile implements Parcelable { * @hide */ public void setEmergencyCallInfo(EmergencyNumber num) { - setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmask()); + setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial()); setEmergencyUrns(num.getEmergencyUrns()); setEmergencyCallRouting(num.getEmergencyCallRouting()); + setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask() + == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST); } /** @@ -843,6 +851,15 @@ public final class ImsCallProfile implements Parcelable { } /** + * Set if this is for testing emergency call, only valid if {@link #getServiceType} returns + * {@link #SERVICE_TYPE_EMERGENCY}. + */ + @VisibleForTesting + public void setEmergencyCallTesting(boolean isTesting) { + mEmergencyCallTesting = isTesting; + } + + /** * Get the emergency service categories, only valid if {@link #getServiceType} returns * {@link #SERVICE_TYPE_EMERGENCY} * @@ -892,4 +909,11 @@ public final class ImsCallProfile implements Parcelable { public @EmergencyCallRouting int getEmergencyCallRouting() { return mEmergencyCallRouting; } + + /** + * Get if the emergency call is for testing purpose. + */ + public boolean isEmergencyCallTesting() { + return mEmergencyCallTesting; + } } diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index e2350fe78500..9414abd98b1c 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -92,7 +92,7 @@ public class ImsMmTelManager { public static final int WIFI_MODE_WIFI_PREFERRED = 2; /** - * Callback class for receiving Registration callback events. + * Callback class for receiving IMS network Registration callback events. * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback) * @see #unregisterImsRegistrationCallback(RegistrationCallback) */ @@ -241,7 +241,8 @@ public class ImsMmTelManager { } /** - * Receives IMS capability status updates from the ImsService. + * Receives IMS capability status updates from the ImsService. This information is also + * available via the {@link #isAvailable(int, int)} method below. * * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback) * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) @@ -290,6 +291,8 @@ public class ImsMmTelManager { * If unavailable, the feature is not able to support the unavailable capability at this * time. * + * This information can also be queried using the {@link #isAvailable(int, int)} API. + * * @param capabilities The new availability of the capabilities. */ public void onCapabilitiesStatusChanged( @@ -304,7 +307,7 @@ public class ImsMmTelManager { /**@hide*/ // Only exposed as public method for compatibility with deprecated ImsManager APIs. // TODO: clean up dependencies and change back to private visibility. - public void setExecutor(Executor executor) { + public final void setExecutor(Executor executor) { mBinder.setExecutor(executor); } } @@ -342,8 +345,7 @@ public class ImsMmTelManager { * Registers a {@link RegistrationCallback} with the system, which will provide registration * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed - * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up - * after a subscription is removed. + * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. * * When the callback is registered, it will initiate the callback c to be called with the * current registration state. @@ -351,6 +353,12 @@ public class ImsMmTelManager { * @param executor The executor the callback events should be run on. * @param c The {@link RegistrationCallback} to be added. * @see #unregisterImsRegistrationCallback(RegistrationCallback) + * @throws IllegalArgumentException if the subscription associated with this callback is not + * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or + * {@link CapabilityCallback} callback. + * @throws IllegalStateException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@CallbackExecutor Executor executor, @@ -370,11 +378,17 @@ public class ImsMmTelManager { } /** - * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning - * up to avoid memory leaks or when the subscription is removed. + * Removes an existing {@link RegistrationCallback}. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. + * * @param c The {@link RegistrationCallback} to be removed. * @see SubscriptionManager.OnSubscriptionsChangedListener * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) + * @throws IllegalArgumentException if the subscription ID associated with this callback is + * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { @@ -389,12 +403,14 @@ public class ImsMmTelManager { } /** - * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability - * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. + * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service + * availability updates for the subscription specified in + * {@link #createForSubscriptionId(Context, int)}. The method {@link #isAvailable(int, int)} + * can also be used to query this information at any time. + * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call - * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up after a - * subscription is removed. + * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. * * When the callback is registered, it will initiate the callback c to be called with the * current capabilities. @@ -402,9 +418,15 @@ public class ImsMmTelManager { * @param executor The executor the callback events should be run on. * @param c The MmTel {@link CapabilityCallback} to be registered. * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) + * @throws IllegalArgumentException if the subscription associated with this callback is not + * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or + * {@link CapabilityCallback} callback. + * @throws IllegalStateException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void registerMmTelCapabilityCallback(@CallbackExecutor Executor executor, + public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor, @NonNull CapabilityCallback c) { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); @@ -421,10 +443,15 @@ public class ImsMmTelManager { } /** - * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning - * up to avoid memory leaks. + * Removes an existing MmTel {@link CapabilityCallback}. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. * @param c The MmTel {@link CapabilityCallback} to be removed. * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) + * @throws IllegalArgumentException if the subscription ID associated with this callback is + * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) { diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 916e282f642e..d37198a3e25d 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -133,33 +133,40 @@ public class ProvisioningManager { } /** - * Register a new {@link Callback} to listen to changes to changes in - * IMS provisioning. Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to - * Subscription changed events and call - * {@link #unregisterProvisioningChangedCallback(Callback)} to clean up after a - * subscription is removed. + * Register a new {@link Callback} to listen to changes to changes in IMS provisioning. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. * @param executor The {@link Executor} to call the callback methods on * @param callback The provisioning callbackto be registered. * @see #unregisterProvisioningChangedCallback(Callback) * @see SubscriptionManager.OnSubscriptionsChangedListener + * @throws IllegalArgumentException if the subscription associated with this callback is not + * active (SIM is not inserted, ESIM inactive) or the subscription is invalid. + * @throws IllegalStateException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor, @NonNull Callback callback) { callback.setExecutor(executor); try { - getITelephony().registerImsProvisioningChangedCallback(mSubId, - callback.getBinder()); + getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** - * Unregister an existing {@link Callback}. Ensure to call this method when cleaning - * up to avoid memory leaks or when the subscription is removed. + * Unregister an existing {@link Callback}. When the subscription associated with this + * callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be + * removed. If this method is called for an inactive subscription, it will result in a no-op. * @param callback The existing {@link Callback} to be removed. * @see #registerProvisioningChangedCallback(Executor, Callback) + * + * @throws IllegalArgumentException if the subscription associated with this callback is + * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull Callback callback) { diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 96f7a1bc22a0..17516bc24aca 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -66,7 +66,8 @@ public class DctConstants { public static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9; public static final int EVENT_ROAMING_ON = BASE + 11; public static final int EVENT_ROAMING_OFF = BASE + 12; - public static final int EVENT_ENABLE_NEW_APN = BASE + 13; + public static final int EVENT_ENABLE_APN = BASE + 13; + public static final int EVENT_DISABLE_APN = BASE + 14; public static final int EVENT_DISCONNECT_DONE = BASE + 15; public static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16; public static final int EVENT_DATA_STALL_ALARM = BASE + 17; @@ -92,6 +93,7 @@ public class DctConstants { public static final int EVENT_DATA_RECONNECT = BASE + 47; public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48; public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49; + public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50; /***** Constants *****/ diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index c5d82c5949f0..f8e4adcb6785 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -41,6 +41,7 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyHistogram; import android.telephony.VisualVoicemailSmsFilterSettings; +import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsConfigCallback; @@ -1776,4 +1777,14 @@ interface ITelephony { * Set the String provisioning value for the provisioning key specified. */ int setImsProvisioningString(int subId, int key, String value); + + /** + * Update Emergency Number List for Test Mode. + */ + void updateEmergencyNumberListTestMode(int action, in EmergencyNumber num); + + /** + * Get the full emergency number list for Test Mode. + */ + List<String> getEmergencyNumberListTestMode(); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 2a648bd8b252..8523554aae63 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -501,4 +501,18 @@ public class TelephonyIntents { */ public static final String ACTION_LINE1_NUMBER_ERROR_DETECTED = "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED"; + + /** + * Broadcast action to notify radio bug. + * + * Requires the READ_PRIVILEGED_PHONE_STATE permission. + * + * @hide + */ + public static final String ACTION_REPORT_RADIO_BUG = + "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG"; + + // ACTION_REPORT_RADIO_BUG extra keys + public static final String EXTRA_SLOT_ID = "slotId"; + public static final String EXTRA_RADIO_BUG_TYPE = "radioBugType"; } diff --git a/test-base/api/current.txt b/test-base/api/current.txt index 7ebd6aa8a4a2..91fcca5cac25 100644 --- a/test-base/api/current.txt +++ b/test-base/api/current.txt @@ -48,6 +48,9 @@ package android.test { method public abstract void startTiming(boolean); } + public abstract deprecated class RepetitiveTest implements java.lang.annotation.Annotation { + } + public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation { } diff --git a/test-base/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java index 6a7130e68e61..13e89d2d92a6 100644 --- a/test-base/src/android/test/RepetitiveTest.java +++ b/test-base/src/android/test/RepetitiveTest.java @@ -26,8 +26,10 @@ import java.lang.annotation.Target; * When the annotation is present, the test method is executed the number of times specified by * numIterations and defaults to 1. * - * {@hide} Not needed for public API. + * @deprecated New tests should be written using the + * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. */ +@Deprecated @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RepetitiveTest { @@ -37,4 +39,4 @@ public @interface RepetitiveTest { * @return The total number of iterations, the default is 1. */ int numIterations() default 1; -}
\ No newline at end of file +} diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp index 833c714f07b0..a69f422b3bdb 100644 --- a/test-legacy/Android.bp +++ b/test-legacy/Android.bp @@ -25,7 +25,7 @@ java_library_static { static_libs: [ "android.test.base-minus-junit", "android.test.runner-minus-junit", - "android.test.mock.impl", + "android.test.mock_static", ], no_framework_libs: true, diff --git a/test-mock/Android.bp b/test-mock/Android.bp index e1d6e01d6d06..43b765d8b783 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -30,3 +30,19 @@ java_sdk_library { srcs_lib_whitelist_pkgs: ["android"], compile_dex: true, } + +// Build the android.test.mock_static library +// ========================================== +// This is only intended for inclusion in the legacy-android-test. +// Must not be used elewhere. +java_library_static { + name: "android.test.mock_static", + + java_version: "1.8", + srcs: ["src/**/*.java"], + + no_framework_libs: true, + libs: [ + "framework", + ], +} diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml index 0be1ea0fdfd2..17eb029166f0 100644 --- a/tests/ActivityViewTest/AndroidManifest.xml +++ b/tests/ActivityViewTest/AndroidManifest.xml @@ -57,5 +57,10 @@ android:exported="true" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"> </activity> + + <activity android:name=".ActivityViewVisibilityActivity" + android:label="AV Visibility" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"> + </activity> </application> </manifest> diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml index ba2e91166440..efcaef679a9c 100644 --- a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml +++ b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml @@ -41,4 +41,10 @@ android:text="Test Resize ActivityView" android:textAllCaps="false"/> + <Button + android:id="@+id/visibility_activity_view_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Test ActivityView Visibility" + android:textAllCaps="false"/> </LinearLayout> diff --git a/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml new file mode 100644 index 000000000000..d29d4dfc0428 --- /dev/null +++ b/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#cfd8dc"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/activity_launch_button" + android:layout_width="200dp" + android:layout_height="wrap_content" + android:text="Launch test activity" /> + + <Spinner + android:id="@+id/visibility_spinner" + android:layout_width="200dp" + android:layout_height="match_parent"/> + + </LinearLayout> + + <ActivityView + android:id="@+id/activity_view" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout> diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java index 66f0c6a56afd..4f09c28fe711 100644 --- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java +++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java @@ -35,5 +35,8 @@ public class ActivityViewMainActivity extends Activity { findViewById(R.id.resize_activity_view_button).setOnClickListener( v -> startActivity(new Intent(this, ActivityViewResizeActivity.class))); + + findViewById(R.id.visibility_activity_view_button).setOnClickListener( + v -> startActivity(new Intent(this, ActivityViewVisibilityActivity.class))); } } diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java index ba2c764bd7d7..52aba2b13c42 100644 --- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java +++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java @@ -24,12 +24,14 @@ import static android.view.MotionEvent.ACTION_UP; import android.app.Activity; import android.content.res.Configuration; import android.os.Bundle; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.TextView; public class ActivityViewTestActivity extends Activity { + private static final String TAG = "ActivityViewTestActivity"; private View mRoot; private TextView mTextView; @@ -84,6 +86,7 @@ public class ActivityViewTestActivity extends Activity { } private void updateStateText(String state) { + Log.d(TAG, state); mTextView.setText(state); } diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java new file mode 100644 index 000000000000..ecd2cf3c578e --- /dev/null +++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.test.activityview; + +import static android.view.View.GONE; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + +import android.app.Activity; +import android.app.ActivityView; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; + +public class ActivityViewVisibilityActivity extends Activity { + private static final String[] sVisibilityOptions = {"VISIBLE", "INVISIBLE", "GONE"}; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_view_visibility_activity); + + final ActivityView activityView = findViewById(R.id.activity_view); + final Button launchButton = findViewById(R.id.activity_launch_button); + launchButton.setOnClickListener(v -> { + final Intent intent = new Intent(this, ActivityViewTestActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + activityView.startActivity(intent); + }); + + final Spinner visibilitySpinner = findViewById(R.id.visibility_spinner); + final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, + android.R.layout.simple_spinner_item, sVisibilityOptions); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + visibilitySpinner.setAdapter(adapter); + visibilitySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + switch (position) { + case 0: + activityView.setVisibility(VISIBLE); + break; + case 1: + activityView.setVisibility(INVISIBLE); + break; + case 2: + activityView.setVisibility(GONE); + break; + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); + } +} diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java index 932fee0c1956..299fbefc78e4 100644 --- a/tests/net/java/android/net/LinkPropertiesTest.java +++ b/tests/net/java/android/net/LinkPropertiesTest.java @@ -849,6 +849,18 @@ public class LinkPropertiesTest { assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed))); } + private void assertParcelingIsLossless(LinkProperties source) { + Parcel p = Parcel.obtain(); + source.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + final byte[] marshalled = p.marshall(); + p = Parcel.obtain(); + p.unmarshall(marshalled, 0, marshalled.length); + p.setDataPosition(0); + LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p); + assertEquals(source, dest); + } + @Test public void testLinkPropertiesParcelable() throws Exception { LinkProperties source = new LinkProperties(); @@ -870,15 +882,12 @@ public class LinkPropertiesTest { source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); - Parcel p = Parcel.obtain(); - source.writeToParcel(p, /* flags */ 0); - p.setDataPosition(0); - final byte[] marshalled = p.marshall(); - p = Parcel.obtain(); - p.unmarshall(marshalled, 0, marshalled.length); - p.setDataPosition(0); - LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p); + assertParcelingIsLossless(source); + } - assertEquals(source, dest); + @Test + public void testParcelUninitialized() throws Exception { + LinkProperties empty = new LinkProperties(); + assertParcelingIsLossless(empty); } } diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java new file mode 100644 index 000000000000..4cabfc95b49d --- /dev/null +++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java @@ -0,0 +1,193 @@ +/* + * 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.shared; + +import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable; +import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable; + +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.ProxyInfo; +import android.net.RouteInfo; +import android.net.Uri; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; + +/** + * Tests for {@link LinkPropertiesParcelableUtil} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LinkPropertiesParcelableUtilTest { + private LinkProperties mLinkProperties; + + private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE"; + private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1"; + private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2"; + + @Before + public void setUp() { + mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE); + mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE)); + mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE)); + } + + private static LinkProperties makeLinkProperties(String iface) { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(iface); + lp.setLinkAddresses(Arrays.asList( + new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16), + new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42))); + lp.setDnsServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::42"), + InetAddresses.parseNumericAddress("192.168.1.1") + )); + lp.setValidatedPrivateDnsServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::43"), + InetAddresses.parseNumericAddress("192.168.42.43") + )); + lp.setPcscfServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::47"), + InetAddresses.parseNumericAddress("192.168.42.47") + )); + lp.setUsePrivateDns(true); + lp.setPrivateDnsServerName("test.example.com"); + lp.setDomains("test1.example.com,test2.example.com"); + lp.addRoute(new RouteInfo( + new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45), + InetAddresses.parseNumericAddress("2001:db8::45"), + iface, + RouteInfo.RTN_UNICAST + )); + lp.addRoute(new RouteInfo( + new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16), + InetAddresses.parseNumericAddress("192.168.45.1"), + iface, + RouteInfo.RTN_THROW + )); + lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000, + "excl1.example.com,excl2.example.com")); + lp.setMtu(5000); + lp.setTcpBufferSizes("1,2,3,4,5,6"); + lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96)); + + // Verify that this test does not miss any new field added later. + // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals + // must also be updated. + assertEquals(14, Arrays.stream(LinkProperties.class.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); + + return lp; + } + + @Test + public void testParcelUnparcel() { + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullInterface() { + mLinkProperties.setInterfaceName(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullPrivateDnsServer() { + mLinkProperties.setPrivateDnsServerName(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullDomains() { + mLinkProperties.setDomains(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullProxy() { + mLinkProperties.setHttpProxy(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullTcpBufferSizes() { + mLinkProperties.setTcpBufferSizes(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyLinkAddresses() { + mLinkProperties.setLinkAddresses(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyDnses() { + mLinkProperties.setDnsServers(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyValidatedPrivateDnses() { + mLinkProperties.setValidatedPrivateDnsServers(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyRoutes() { + for (RouteInfo r : mLinkProperties.getAllRoutes()) { + mLinkProperties.removeRoute(r); + } + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_PacFileProxyInfo() { + mLinkProperties.setHttpProxy(new ProxyInfo(Uri.parse("http://pacfile.example.com"))); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullNat64Prefix() { + mLinkProperties.setNat64Prefix(null); + doParcelUnparcelTest(); + } + + private void doParcelUnparcelTest() { + final LinkProperties unparceled = fromStableParcelable(toStableParcelable(mLinkProperties)); + assertLinkPropertiesEquals(mLinkProperties, unparceled); + } + + private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) { + assertEquals(expected, actual); + + // LinkProperties equals() does not include stacked links + assertEquals(expected.getStackedLinks(), actual.getStackedLinks()); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index bf3964416e11..2a92a7dabd98 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -4683,7 +4683,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.connect(true); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME); + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME); Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent); // Clat iface up, expect stack link updated. @@ -4710,7 +4710,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); - verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME); + verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); // Clat iface removed, expect linkproperties revert to original one clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 4c52d818269d..9578ded1a089 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -32,11 +32,13 @@ import android.app.PendingIntent; import android.content.Context; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.INetd; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMisc; import android.net.NetworkStack; +import android.os.INetworkManagementService; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.format.DateUtils; @@ -66,6 +68,8 @@ public class LingerMonitorTest { LingerMonitor mMonitor; @Mock ConnectivityService mConnService; + @Mock INetd mNetd; + @Mock INetworkManagementService mNMS; @Mock Context mCtx; @Mock NetworkMisc mMisc; @Mock NetworkNotificationManager mNotifier; @@ -352,7 +356,7 @@ public class LingerMonitorTest { caps.addCapability(0); caps.addTransportType(transport); NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null, - caps, 50, mCtx, null, mMisc, mConnService); + caps, 50, mCtx, null, mMisc, mConnService, mNetd, mNMS); nai.everValidated = true; return nai; } diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java index bf42412d68d8..07b1d057c882 100644 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java @@ -17,9 +17,7 @@ package com.android.server.connectivity; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -27,6 +25,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.ConnectivityManager; +import android.net.INetd; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -57,6 +56,7 @@ public class Nat464XlatTest { @Mock ConnectivityService mConnectivity; @Mock NetworkMisc mMisc; + @Mock INetd mNetd; @Mock INetworkManagementService mNms; @Mock InterfaceConfiguration mConfig; @Mock NetworkAgentInfo mNai; @@ -65,7 +65,7 @@ public class Nat464XlatTest { Handler mHandler; Nat464Xlat makeNat464Xlat() { - return new Nat464Xlat(mNms, mNai); + return new Nat464Xlat(mNai, mNetd, mNms); } @Before @@ -129,7 +129,7 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); @@ -144,7 +144,7 @@ public class Nat464XlatTest { // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). nat.stop(); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); // Stacked interface removed notification arrives. nat.interfaceRemoved(STACKED_IFACE); @@ -156,7 +156,7 @@ public class Nat464XlatTest { assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertIdle(nat); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } @Test @@ -168,7 +168,7 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); @@ -185,7 +185,7 @@ public class Nat464XlatTest { mLooper.dispatchNext(); verify(mNms).unregisterObserver(eq(nat)); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); assertTrue(c.getValue().getStackedLinks().isEmpty()); assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); @@ -194,7 +194,7 @@ public class Nat464XlatTest { // ConnectivityService stops clat: no-op. nat.stop(); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } @Test @@ -205,13 +205,13 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); verify(mNms).unregisterObserver(eq(nat)); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); assertIdle(nat); // In-flight interface up notification arrives: no-op @@ -225,7 +225,7 @@ public class Nat464XlatTest { assertIdle(nat); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } @Test @@ -236,16 +236,16 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); verify(mNms).unregisterObserver(eq(nat)); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); assertIdle(nat); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } static void assertIdle(Nat464Xlat nat) { diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java index 859a54d29321..e63c3b02d1c3 100644 --- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java @@ -16,6 +16,9 @@ package com.android.server.net.ipmemorystore; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; + import android.content.Context; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -26,6 +29,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.File; + /** Unit tests for {@link IpMemoryStoreServiceTest}. */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -36,6 +41,7 @@ public class IpMemoryStoreServiceTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + doReturn(new File("/tmp/test.db")).when(mMockContext).getDatabasePath(anyString()); } @Test diff --git a/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java new file mode 100644 index 000000000000..8d367e2fc387 --- /dev/null +++ b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java @@ -0,0 +1,149 @@ +/* + * 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.net.ipmemorystore; + +import static com.android.server.net.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link RelevanceUtils}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RelevanceUtilsTests { + @Test + public void testComputeRelevanceForTargetDate() { + final long dayInMillis = 24L * 60 * 60 * 1000; + final long base = 1_000_000L; // any given point in time + // Relevance when the network expires in 1000 years must be capped + assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate( + base + 1000L * dayInMillis, base)); + // Relevance when expiry is before the date must be 0 + assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base)); + // Make sure the relevance for a given target date is higher if the expiry is further + // in the future + assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base) + < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base)); + + // Make sure the relevance falls slower as the expiry is closing in. This is to ensure + // the decay is indeed logarithmic. + final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base); + final int relevance50DaysBeforeExpiry = + RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base); + final int relevance100DaysBeforeExpiry = + RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base); + final int relevance150DaysBeforeExpiry = + RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base); + assertEquals(0, relevanceAtExpiry); + assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry + < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry); + assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry + < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry); + } + + @Test + public void testIncreaseRelevance() { + long expiry = System.currentTimeMillis(); + + final long firstBump = RelevanceUtils.bumpExpiryDate(expiry); + // Though a few milliseconds might have elapsed, the first bump should push the duration + // to days in the future, so unless this test takes literal days between these two lines, + // this should always pass. + assertTrue(firstBump > expiry); + + expiry = 0; + long lastDifference = Long.MAX_VALUE; + // The relevance should be capped in at most this many steps. Otherwise, fail. + final int steps = 1000; + for (int i = 0; i < steps; ++i) { + final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry); + if (newExpiry == expiry) { + // The relevance should be capped. Make sure it is, then exit without failure. + assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); + return; + } + // Make sure the new expiry is further in the future than last time. + assertTrue(newExpiry > expiry); + // Also check that it was not bumped as much as the last bump, because the + // decay must be exponential. + assertTrue(newExpiry - expiry < lastDifference); + lastDifference = newExpiry - expiry; + expiry = newExpiry; + } + fail("Relevance failed to go to the maximum value after " + steps + " bumps"); + } + + @Test + public void testContinuity() { + final long expiry = System.currentTimeMillis(); + + // Relevance at expiry and after expiry should be the cap. + final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, + expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000)); + assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE); + final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, + expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); + assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE); + + // If the max relevance is reached at the cap lifetime, one millisecond less than this + // should be very close. Strictly speaking this is a bit brittle, but it should be + // good enough for the purposes of the memory store. + final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate( + expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1); + assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE); + assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10); + + // Likewise the relevance one millisecond before expiry should be very close to 0. It's + // fine if it rounds down to 0. + final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate( + expiry, expiry - 1); + assertTrue(relevanceOneMillisecBeforeExpiry <= 10); + assertTrue(relevanceOneMillisecBeforeExpiry >= 0); + + final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry); + assertEquals(relevanceAtExpiry, 0); + final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, + expiry + 1_000_000); + assertEquals(relevanceAfterExpiry, 0); + } + + // testIncreaseRelevance makes sure bumping the expiry continuously always yields a + // monotonically increasing date as a side effect, but this tests that the relevance (as + // opposed to the expiry date) increases monotonically with increasing periods. + @Test + public void testMonotonicity() { + // Hopefully the relevance is granular enough to give a different value for every one + // of this number of steps. + final int steps = 40; + final long expiry = System.currentTimeMillis(); + + int lastRelevance = -1; + for (int i = 0; i < steps; ++i) { + final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps); + final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date); + assertTrue(relevance > lastRelevance); + lastRelevance = relevance; + } + } +} diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 58702dc465cc..e0d2f48e8dcf 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -1164,8 +1164,6 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource current_policies |= OverlayableItem::Policy::kPublic; } else if (trimmed_part == "product") { current_policies |= OverlayableItem::Policy::kProduct; - } else if (trimmed_part == "product_services") { - current_policies |= OverlayableItem::Policy::kProductServices; } else if (trimmed_part == "system") { current_policies |= OverlayableItem::Policy::kSystem; } else if (trimmed_part == "vendor") { diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index debca9c1e1ba..827c7deaf452 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -935,9 +935,6 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) { <policy type="product"> <item type="string" name="bar" /> </policy> - <policy type="product_services"> - <item type="string" name="baz" /> - </policy> <policy type="system"> <item type="string" name="fiz" /> </policy> @@ -966,14 +963,6 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) { EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct)); - search_result = table_.FindResource(test::ParseNameOrDie("string/baz")); - ASSERT_TRUE(search_result); - ASSERT_THAT(search_result.value().entry, NotNull()); - ASSERT_TRUE(search_result.value().entry->overlayable_item); - result_overlayable_item = search_result.value().entry->overlayable_item.value(); - EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); - EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices)); - search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); @@ -1028,7 +1017,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { std::string input = R"( <overlayable name="Name"> - <policy type="vendor|product_services"> + <policy type="vendor|public"> <item type="string" name="foo" /> </policy> <policy type="product|system"> @@ -1044,7 +1033,7 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor - | OverlayableItem::Policy::kProductServices)); + | OverlayableItem::Policy::kPublic)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); @@ -1139,7 +1128,7 @@ TEST_F(ResourceParserTest, NestPolicyInOverlayableError) { std::string input = R"( <overlayable name="Name"> <policy type="vendor|product"> - <policy type="product_services"> + <policy type="public"> <item type="string" name="foo" /> </policy> </policy> diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index eaf6a47a15fd..7ca99ea42b50 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -92,9 +92,6 @@ struct OverlayableItem { // The resource can be overlaid by any overlay on the product partition. kProduct = 0x08, - - // The resource can be overlaid by any overlay on the product services partition. - kProductServices = 0x10 }; std::shared_ptr<Overlayable> overlayable; diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp index a733134f123c..b97dc6b205ca 100644 --- a/tools/aapt2/ResourceTable_test.cpp +++ b/tools/aapt2/ResourceTable_test.cpp @@ -248,7 +248,7 @@ TEST(ResourceTableTest, SetOverlayable) { Source("res/values/overlayable.xml", 40)); OverlayableItem overlayable_item(overlayable); overlayable_item.policies |= OverlayableItem::Policy::kProduct; - overlayable_item.policies |= OverlayableItem::Policy::kProductServices; + overlayable_item.policies |= OverlayableItem::Policy::kVendor; overlayable_item.comment = "comment"; overlayable_item.source = Source("res/values/overlayable.xml", 42); @@ -265,7 +265,7 @@ TEST(ResourceTableTest, SetOverlayable) { EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml")); EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct - | OverlayableItem::Policy::kProductServices)); + | OverlayableItem::Policy::kVendor)); ASSERT_THAT(result_overlayable_item.comment, StrEq("comment")); EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml")); EXPECT_THAT(result_overlayable_item.source.line, 42); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index c6f91527c91c..ab4805f626a5 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -16,6 +16,7 @@ #include "ResourceUtils.h" +#include <algorithm> #include <sstream> #include "android-base/stringprintf.h" @@ -503,6 +504,14 @@ Maybe<int> ParseSdkVersion(const StringPiece& str) { if (entry.first == trimmed_str) { return entry.second; } + + // Try parsing codename from "[codename].[preview_sdk_fingerprint]" value. + const StringPiece::const_iterator begin = std::begin(trimmed_str); + const StringPiece::const_iterator end = std::end(trimmed_str); + const StringPiece::const_iterator codename_end = std::find(begin, end, '.'); + if (codename_end != end && entry.first == trimmed_str.substr(begin, codename_end)) { + return entry.second; + } return {}; } diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index 5ce464074335..9018b0fc372a 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -16,6 +16,7 @@ #include "ResourceUtils.h" +#include "SdkConstants.h" #include "Resource.h" #include "test/Test.h" @@ -212,6 +213,17 @@ TEST(ResourceUtilsTest, ItemsWithWhitespaceAreParsedCorrectly) { Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened)))); } +TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) { + const android::StringPiece codename = + GetDevelopmentSdkCodeNameAndVersion().first; + const int version = GetDevelopmentSdkCodeNameAndVersion().second; + + EXPECT_THAT(ResourceUtils::ParseSdkVersion(codename), Eq(Maybe<int>(version))); + EXPECT_THAT( + ResourceUtils::ParseSdkVersion(codename.to_string() + ".fingerprint"), + Eq(Maybe<int>(version))); +} + TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) { EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys ") diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index da541be9502b..73b568e77689 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -155,7 +155,6 @@ message OverlayableItem { SYSTEM = 1; VENDOR = 2; PRODUCT = 3; - PRODUCT_SERVICES = 4; } // The location of the <item> declaration in source. diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index 7d4c6f348403..40aaa05c2b30 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -473,10 +473,6 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) { policies |= OverlayableItem::Policy::kProduct; } - if (policy_header->policy_flags - & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) { - policies |= OverlayableItem::Policy::kProductServices; - } const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>( ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize)); diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index c4ecbafc008b..9d341cc1ca4a 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -485,9 +485,6 @@ class PackageFlattener { if (item.policies & OverlayableItem::Policy::kProduct) { policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION; } - if (item.policies & OverlayableItem::Policy::kProductServices) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION; - } } auto policy = overlayable_chunk->policy_ids.find(policy_flags); diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index 18fecf60c977..ddc117399390 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -660,12 +660,10 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { OverlayableItem overlayable_item_zero(overlayable); overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct; overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem; - overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices; std::string name_one = "com.app.test:integer/overlayable_one_item"; OverlayableItem overlayable_item_one(overlayable); overlayable_item_one.policies |= OverlayableItem::Policy::kPublic; - overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices; std::string name_two = "com.app.test:integer/overlayable_two_item"; OverlayableItem overlayable_item_two(overlayable); @@ -698,16 +696,14 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem - | OverlayableItem::Policy::kProduct - | OverlayableItem::Policy::kProductServices); + | OverlayableItem::Policy::kProduct); search_result = output_table.FindResource(test::ParseNameOrDie(name_one)); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable_item); overlayable_item = search_result.value().entry->overlayable_item.value(); - EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic - | OverlayableItem::Policy::kProductServices); + EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic); search_result = output_table.FindResource(test::ParseNameOrDie(name_two)); ASSERT_TRUE(search_result); @@ -735,13 +731,11 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { OverlayableItem overlayable_item_zero(group); overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct; overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem; - overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices; auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization"); std::string name_one = "com.app.test:integer/overlayable_one"; OverlayableItem overlayable_item_one(group_one); overlayable_item_one.policies |= OverlayableItem::Policy::kPublic; - overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices; std::string name_two = "com.app.test:integer/overlayable_two"; OverlayableItem overlayable_item_two(group); @@ -773,8 +767,7 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { EXPECT_EQ(result_overlayable.overlayable->name, "TestName"); EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme"); EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem - | OverlayableItem::Policy::kProduct - | OverlayableItem::Policy::kProductServices); + | OverlayableItem::Policy::kProduct); search_result = output_table.FindResource(test::ParseNameOrDie(name_one)); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); @@ -782,8 +775,7 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { result_overlayable = search_result.value().entry->overlayable_item.value(); EXPECT_EQ(result_overlayable.overlayable->name, "OtherName"); EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization"); - EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic - | OverlayableItem::Policy::kProductServices); + EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic); search_result = output_table.FindResource(test::ParseNameOrDie(name_two)); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index 6b5746d63bf8..aff1b391f861 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -390,9 +390,6 @@ bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable, case pb::OverlayableItem::PRODUCT: out_overlayable->policies |= OverlayableItem::Policy::kProduct; break; - case pb::OverlayableItem::PRODUCT_SERVICES: - out_overlayable->policies |= OverlayableItem::Policy::kProductServices; - break; default: *out_error = "unknown overlayable policy"; return false; diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index 76fbb464b62a..b549e2369f98 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -303,9 +303,6 @@ static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item if (overlayable_item.policies & OverlayableItem::Policy::kProduct) { pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT); } - if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) { - pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES); - } if (overlayable_item.policies & OverlayableItem::Policy::kSystem) { pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM); } diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index 4a3c1b86236e..cce3939704cf 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -519,7 +519,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>( "TaskBar", "overlay://theme")); - overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices; + overlayable_item_bar.policies |= OverlayableItem::Policy::kPublic; overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor; OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>( @@ -565,7 +565,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar")); EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme")); - EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices + EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic | OverlayableItem::Policy::kVendor)); search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz")); diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 85bf6f218ba0..5812ec44f1fb 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -213,6 +213,27 @@ static bool VerifyUsesFeature(xml::Element* el, SourcePathDiagnostics* diag) { return true; } +static bool AddDeprecatedUsesFeatures(xml::Element* el, SourcePathDiagnostics* diag) { + if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) { + if (attr->value.empty()) { + return true; + } + + // Add "android.hardware.fingerprint" when "android.hardware.biometric.fingerprint" is found, + // since the former is deprecated in Q and the latter is not present pre-Q. (see b/115639644) + if (attr->value == "android.hardware.biometrics.fingerprint") { + auto element = el->CloneElement([&](const xml::Element& el, xml::Element* out_el) { + xml::Attribute* cloned_attr = out_el->FindOrCreateAttribute(xml::kSchemaAndroid, "name"); + cloned_attr->value = "android.hardware.fingerprint"; + }); + + el->parent->AppendChild(std::move(element)); + } + } + + return true; +} + bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) { // First verify some options. @@ -247,6 +268,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, // Common <uses-feature> actions. xml::XmlNodeAction uses_feature_action; uses_feature_action.Action(VerifyUsesFeature); + uses_feature_action.Action(AddDeprecatedUsesFeatures); // Common component actions. xml::XmlNodeAction component_action; diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index adea6273bc8b..fcc9f559a73f 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -832,4 +832,36 @@ TEST_F(ManifestFixerTest, UsesLibraryMustHaveNonEmptyName) { EXPECT_THAT(Verify(input), NotNull()); } +TEST_F(ManifestFixerTest, UsesFeatureAddDeprecated) { + std::string input = R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> + <uses-feature android:name="android.hardware.biometrics.fingerprint" /> + <feature-group> + <uses-feature android:name="android.hardware.biometrics.fingerprint" /> + </feature-group> + </manifest>)"; + + std::unique_ptr<xml::XmlResource> manifest = Verify(input); + ASSERT_THAT(manifest, NotNull()); + EXPECT_THAT(manifest->root->FindChildWithAttribute("", "uses-feature", + xml::kSchemaAndroid, "name", + "android.hardware.biometrics.fingerprint"), + Ne(nullptr)); + EXPECT_THAT(manifest->root->FindChildWithAttribute("", "uses-feature", + xml::kSchemaAndroid, "name", + "android.hardware.fingerprint"), + Ne(nullptr)); + + xml::Element* feature_group = manifest->root->FindChild("", "feature-group"); + ASSERT_THAT(feature_group, Ne(nullptr)); + + EXPECT_THAT(feature_group->FindChildWithAttribute("", "uses-feature", xml::kSchemaAndroid, "name", + "android.hardware.biometrics.fingerprint"), + Ne(nullptr)); + EXPECT_THAT(feature_group->FindChildWithAttribute("", "uses-feature", xml::kSchemaAndroid, "name", + "android.hardware.fingerprint"), + Ne(nullptr)); +} + } // namespace aapt diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index 921d634e583e..ad3674e16774 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -484,7 +484,7 @@ TEST_F(TableMergerTest, SetOverlayableLater) { OverlayableItem overlayable_item(overlayable); overlayable_item.policies |= OverlayableItem::Policy::kPublic; - overlayable_item.policies |= OverlayableItem::Policy::kProductServices; + overlayable_item.policies |= OverlayableItem::Policy::kSystem; std::unique_ptr<ResourceTable> table_b = test::ResourceTableBuilder() .SetPackageId("com.app.a", 0x7f) @@ -506,7 +506,7 @@ TEST_F(TableMergerTest, SetOverlayableLater) { EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic - | OverlayableItem::Policy::kProductServices)); + | OverlayableItem::Policy::kSystem)); } TEST_F(TableMergerTest, SameResourceDifferentNameFail) { diff --git a/wifi/java/android/net/wifi/DppStatusCallback.java b/wifi/java/android/net/wifi/DppStatusCallback.java deleted file mode 100644 index fa2ab30ef479..000000000000 --- a/wifi/java/android/net/wifi/DppStatusCallback.java +++ /dev/null @@ -1,166 +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.net.wifi; - -import android.annotation.IntDef; -import android.annotation.SystemApi; -import android.os.Handler; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * DPP Status Callback. Use this callback to get status updates (success, failure, progress) - * from the DPP operation started with {@link WifiManager#startDppAsConfiguratorInitiator(String, - * int, int, Handler, DppStatusCallback)} or {@link WifiManager#startDppAsEnrolleeInitiator(String, - * Handler, DppStatusCallback)} - * @hide - */ -@SystemApi -public abstract class DppStatusCallback { - /** - * DPP Success event: Configuration sent (Configurator mode). - */ - public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; - - /** @hide */ - @IntDef(prefix = { "DPP_EVENT_SUCCESS_" }, value = { - DPP_EVENT_SUCCESS_CONFIGURATION_SENT, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DppSuccessStatusCode {} - - /** - * DPP Progress event: Initial authentication with peer succeeded. - */ - public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; - - /** - * DPP Progress event: Peer requires more time to process bootstrapping. - */ - public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; - - /** @hide */ - @IntDef(prefix = { "DPP_EVENT_PROGRESS_" }, value = { - DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS, - DPP_EVENT_PROGRESS_RESPONSE_PENDING, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DppProgressStatusCode {} - - /** - * DPP Failure event: Scanned QR code is either not a DPP URI, or the DPP URI has errors. - */ - public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; - - /** - * DPP Failure event: Bootstrapping/Authentication initialization process failure. - */ - public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; - - /** - * DPP Failure event: Both devices are implementing the same role and are incompatible. - */ - public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; - - /** - * DPP Failure event: Configuration process has failed due to malformed message. - */ - public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; - - /** - * DPP Failure event: DPP request while in another DPP exchange. - */ - public static final int DPP_EVENT_FAILURE_BUSY = -5; - - /** - * DPP Failure event: No response from the peer. - */ - public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; - - /** - * DPP Failure event: General protocol failure. - */ - public static final int DPP_EVENT_FAILURE = -7; - - /** - * DPP Failure event: Feature or option is not supported. - */ - public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; - - /** - * DPP Failure event: Invalid network provided to DPP configurator. - * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK). - */ - public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; - - - /** @hide */ - @IntDef(prefix = {"DPP_EVENT_FAILURE_"}, value = { - DPP_EVENT_FAILURE_INVALID_URI, - DPP_EVENT_FAILURE_AUTHENTICATION, - DPP_EVENT_FAILURE_NOT_COMPATIBLE, - DPP_EVENT_FAILURE_CONFIGURATION, - DPP_EVENT_FAILURE_BUSY, - DPP_EVENT_FAILURE_TIMEOUT, - DPP_EVENT_FAILURE, - DPP_EVENT_FAILURE_NOT_SUPPORTED, - DPP_EVENT_FAILURE_INVALID_NETWORK, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DppFailureStatusCode { - } - - /** - * Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the - * peer DPP configurator. This callback marks the successful end of the DPP current DPP - * session, and no further callbacks will be called. This callback is the successful outcome - * of a DPP flow starting with {@link WifiManager#startDppAsEnrolleeInitiator(String, Handler, - * DppStatusCallback)}. - * - * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator - */ - public abstract void onEnrolleeSuccess(int newNetworkId); - - /** - * Called when a DPP success event takes place, except for when configuration is received from - * an external Configurator. The callback onSuccessConfigReceived will be used in this case. - * This callback marks the successful end of the current DPP session, and no further - * callbacks will be called. This callback is the successful outcome of a DPP flow starting with - * {@link WifiManager#startDppAsConfiguratorInitiator(String, int, int, Handler, - * DppStatusCallback)}. - * - * @param code DPP success status code. - */ - public abstract void onConfiguratorSuccess(@DppSuccessStatusCode int code); - - /** - * Called when a DPP Failure event takes place. This callback marks the unsuccessful end of the - * current DPP session, and no further callbacks will be called. - * - * @param code DPP failure status code. - */ - public abstract void onFailure(@DppFailureStatusCode int code); - - /** - * Called when DPP events that indicate progress take place. Can be used by UI elements - * to show progress. - * - * @param code DPP progress status code. - */ - public abstract void onProgress(@DppProgressStatusCode int code); -} diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java new file mode 100644 index 000000000000..3b4a6cd7508c --- /dev/null +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.os.Handler; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure, + * progress) from the Easy Connect operation started with + * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, + * int, int, Handler, EasyConnectStatusCallback)} or + * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, + * Handler, EasyConnectStatusCallback)} + * + * @hide + */ +@SystemApi +public abstract class EasyConnectStatusCallback { + /** + * Easy Connect Success event: Configuration sent (Configurator mode). + */ + public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = { + EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectSuccessStatusCode { + } + + /** + * Easy Connect Progress event: Initial authentication with peer succeeded. + */ + public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; + + /** + * Easy Connect Progress event: Peer requires more time to process bootstrapping. + */ + public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = { + EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS, + EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectProgressStatusCode { + } + + /** + * Easy Connect Failure event: Scanned QR code is either not a Easy Connect URI, or the Easy + * Connect URI has errors. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; + + /** + * Easy Connect Failure event: Bootstrapping/Authentication initialization process failure. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; + + /** + * Easy Connect Failure event: Both devices are implementing the same role and are incompatible. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; + + /** + * Easy Connect Failure event: Configuration process has failed due to malformed message. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; + + /** + * Easy Connect Failure event: Easy Connect request while in another Easy Connect exchange. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; + + /** + * Easy Connect Failure event: No response from the peer. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; + + /** + * Easy Connect Failure event: General protocol failure. + */ + public static final int EASY_CONNECT_EVENT_FAILURE = -7; + + /** + * Easy Connect Failure event: Feature or option is not supported. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; + + /** + * Easy Connect Failure event: Invalid network provided to Easy Connect configurator. + * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK). + */ + public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; + + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = { + EASY_CONNECT_EVENT_FAILURE_INVALID_URI, + EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION, + EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE, + EASY_CONNECT_EVENT_FAILURE_CONFIGURATION, + EASY_CONNECT_EVENT_FAILURE_BUSY, + EASY_CONNECT_EVENT_FAILURE_TIMEOUT, + EASY_CONNECT_EVENT_FAILURE, + EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED, + EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectFailureStatusCode { + } + + /** + * Called when local Easy Connect Enrollee successfully receives a new Wi-Fi configuration from + * the + * peer Easy Connect configurator. This callback marks the successful end of the Easy Connect + * current Easy Connect + * session, and no further callbacks will be called. This callback is the successful outcome + * of a Easy Connect flow starting with + * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, + * Handler, + * EasyConnectStatusCallback)}. + * + * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator + */ + public abstract void onEnrolleeSuccess(int newNetworkId); + + /** + * Called when a Easy Connect success event takes place, except for when configuration is + * received from + * an external Configurator. The callback onSuccessConfigReceived will be used in this case. + * This callback marks the successful end of the current Easy Connect session, and no further + * callbacks will be called. This callback is the successful outcome of a Easy Connect flow + * starting with + * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler, + * EasyConnectStatusCallback)}. + * + * @param code Easy Connect success status code. + */ + public abstract void onConfiguratorSuccess(@EasyConnectSuccessStatusCode int code); + + /** + * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful + * end of the + * current Easy Connect session, and no further callbacks will be called. + * + * @param code Easy Connect failure status code. + */ + public abstract void onFailure(@EasyConnectFailureStatusCode int code); + + /** + * Called when Easy Connect events that indicate progress take place. Can be used by UI elements + * to show progress. + * + * @param code Easy Connect progress status code. + */ + public abstract void onProgress(@EasyConnectProgressStatusCode int code); +} diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 28dd9b471323..e019f282cd33 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -145,11 +145,23 @@ public class WifiConfiguration implements Parcelable { */ public static final int SUITE_B_192 = 10; + /** + * WPA pre-shared key with stronger SHA256-based algorithms. + * @hide + */ + public static final int WPA_PSK_SHA256 = 11; + + /** + * WPA using EAP authentication with stronger SHA256-based algorithms. + * @hide + */ + public static final int WPA_EAP_SHA256 = 12; + public static final String varName = "key_mgmt"; public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP", - "SAE", "OWE", "SUITE_B_192"}; + "SAE", "OWE", "SUITE_B_192", "WPA_PSK_SHA256", "WPA_EAP_SHA256" }; } /** @@ -1886,6 +1898,7 @@ public class WifiConfiguration implements Parcelable { if (creatorName != null) sbuf.append(" cname=" + creatorName); if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid); if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName); + if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier); sbuf.append(" lcuid=" + lastConnectUid); sbuf.append(" userApproved=" + userApprovedAsString(userApproved)); sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected); @@ -2281,6 +2294,7 @@ public class WifiConfiguration implements Parcelable { mRandomizedMacAddress = source.mRandomizedMacAddress; macRandomizationSetting = source.macRandomizationSetting; requirePMF = source.requirePMF; + updateIdentifier = source.updateIdentifier; } } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 840af5d5cd06..35fba3dcf7cf 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; @@ -470,6 +471,7 @@ public class WifiInfo implements Parcelable { } /** {@hide} */ + @SystemApi public boolean isOsuAp() { return mOsuAp; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 517bf3b47a7a..801342dc0c6e 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -236,9 +236,11 @@ public class WifiManager { public static final int WIFI_CREDENTIAL_FORGOT = 1; /** @hide */ + @SystemApi public static final int PASSPOINT_HOME_NETWORK = 0; /** @hide */ + @SystemApi public static final int PASSPOINT_ROAMING_NETWORK = 1; /** @@ -1219,7 +1221,11 @@ public class WifiManager { * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> getAllMatchingWifiConfigs( @NonNull List<ScanResult> scanResults) { List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>(); @@ -1258,7 +1264,11 @@ public class WifiManager { * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders( List<ScanResult> scanResults) { try { @@ -1281,7 +1291,11 @@ public class WifiManager { * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders( @NonNull Set<OsuProvider> osuProviders) { try { @@ -1727,7 +1741,13 @@ public class WifiManager { * @param fqdn The FQDN of the Passpoint configuration to be removed * @throws IllegalArgumentException if no configuration is associated with the given FQDN. * @throws UnsupportedOperationException if Passpoint is not enabled on the device. + * @deprecated This is no longer supported. */ + @Deprecated + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public void removePasspointConfiguration(String fqdn) { try { if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) { @@ -1745,7 +1765,13 @@ public class WifiManager { * * @return A list of {@link PasspointConfiguration} * @throws UnsupportedOperationException if Passpoint is not enabled on the device. + * @deprecated This is no longer supported. */ + @Deprecated + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public List<PasspointConfiguration> getPasspointConfigurations() { try { return mService.getPasspointConfigurations(); @@ -4323,6 +4349,11 @@ public class WifiManager { * @param callback {@link ProvisioningCallback} for updates regarding provisioning flow * @hide */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public void startSubscriptionProvisioning(OsuProvider provider, ProvisioningCallback callback, @Nullable Handler handler) { Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); @@ -4586,93 +4617,100 @@ public class WifiManager { } } - /* DPP - Device Provisioning Protocol AKA "Easy Connect" */ + /* Easy Connect - AKA Device Provisioning Protocol (DPP) */ /** - * DPP Network role: Station. + * Easy Connect Network role: Station. + * * @hide */ @SystemApi - public static final int DPP_NETWORK_ROLE_STA = 0; + public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; /** - * DPP Network role: Access Point. + * Easy Connect Network role: Access Point. + * * @hide */ @SystemApi - public static final int DPP_NETWORK_ROLE_AP = 1; + public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; /** @hide */ - @IntDef(prefix = {"DPP_NETWORK_ROLE_"}, value = { - DPP_NETWORK_ROLE_STA, - DPP_NETWORK_ROLE_AP, + @IntDef(prefix = {"EASY_CONNECT_NETWORK_ROLE_"}, value = { + EASY_CONNECT_NETWORK_ROLE_STA, + EASY_CONNECT_NETWORK_ROLE_AP, }) @Retention(RetentionPolicy.SOURCE) - public @interface DppNetworkRole {} + public @interface EasyConnectNetworkRole { + } /** - * Start DPP in Configurator-Initiator role. The current device will initiate DPP bootstrapping - * with a peer, and configure the peer with the SSID and password of the specified network using - * the DPP protocol on an encrypted link. + * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate + * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of + * the specified network using the Easy Connect protocol on an encrypted link. * - * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning) - * @param selectedNetworkId Selected network ID to be sent to the peer + * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning) + * @param selectedNetworkId Selected network ID to be sent to the peer * @param enrolleeNetworkRole The network role of the enrollee - * @param callback Callback for status updates - * @param handler The handler on whose thread to execute the callbacks. Null for main thread. + * @param callback Callback for status updates + * @param handler The handler on whose thread to execute the callbacks. Null for + * main thread. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) - public void startDppAsConfiguratorInitiator(@NonNull String enrolleeUri, - int selectedNetworkId, @DppNetworkRole int enrolleeNetworkRole, - @Nullable Handler handler, @NonNull DppStatusCallback callback) { + public void startEasyConnectAsConfiguratorInitiator(@NonNull String enrolleeUri, + int selectedNetworkId, @EasyConnectNetworkRole int enrolleeNetworkRole, + @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) { Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId, - enrolleeNetworkRole, new DppCallbackProxy(looper, callback)); + enrolleeNetworkRole, new EasyConnectCallbackProxy(looper, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Start DPP in Enrollee-Initiator role. The current device will initiate DPP bootstrapping - * with a peer, and receive the SSID and password from the peer configurator. + * Start Easy Connect (DPP) in Enrollee-Initiator role. The current device will initiate Easy + * Connect bootstrapping with a peer, and receive the SSID and password from the peer + * configurator. * * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning) - * @param callback Callback for status updates - * @param handler The handler on whose thread to execute the callbacks. Null for main thread. + * @param callback Callback for status updates + * @param handler The handler on whose thread to execute the callbacks. Null for main + * thread. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) - public void startDppAsEnrolleeInitiator(@NonNull String configuratorUri, - @Nullable Handler handler, @NonNull DppStatusCallback callback) { + public void startEasyConnectAsEnrolleeInitiator(@NonNull String configuratorUri, + @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) { Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { mService.startDppAsEnrolleeInitiator(binder, configuratorUri, - new DppCallbackProxy(looper, callback)); + new EasyConnectCallbackProxy(looper, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Stop or abort a current DPP session. + * Stop or abort a current Easy Connect (DPP) session. + * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) - public void stopDppSession() { + public void stopEasyConnectSession() { try { /* Request lower layers to stop/abort and clear resources */ mService.stopDppSession(); @@ -4682,48 +4720,50 @@ public class WifiManager { } /** - * Helper class to support DPP callbacks + * Helper class to support Easy Connect (DPP) callbacks + * * @hide */ @SystemApi - private static class DppCallbackProxy extends IDppCallback.Stub { + private static class EasyConnectCallbackProxy extends IDppCallback.Stub { private final Handler mHandler; - private final DppStatusCallback mDppStatusCallback; + private final EasyConnectStatusCallback mEasyConnectStatusCallback; - DppCallbackProxy(Looper looper, DppStatusCallback dppStatusCallback) { + EasyConnectCallbackProxy(Looper looper, + EasyConnectStatusCallback easyConnectStatusCallback) { mHandler = new Handler(looper); - mDppStatusCallback = dppStatusCallback; + mEasyConnectStatusCallback = easyConnectStatusCallback; } @Override public void onSuccessConfigReceived(int newNetworkId) { - Log.d(TAG, "DPP onSuccessConfigReceived callback"); + Log.d(TAG, "Easy Connect onSuccessConfigReceived callback"); mHandler.post(() -> { - mDppStatusCallback.onEnrolleeSuccess(newNetworkId); + mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId); }); } @Override public void onSuccess(int status) { - Log.d(TAG, "DPP onSuccess callback"); + Log.d(TAG, "Easy Connect onSuccess callback"); mHandler.post(() -> { - mDppStatusCallback.onConfiguratorSuccess(status); + mEasyConnectStatusCallback.onConfiguratorSuccess(status); }); } @Override public void onFailure(int status) { - Log.d(TAG, "DPP onFailure callback"); + Log.d(TAG, "Easy Connect onFailure callback"); mHandler.post(() -> { - mDppStatusCallback.onFailure(status); + mEasyConnectStatusCallback.onFailure(status); }); } @Override public void onProgress(int status) { - Log.d(TAG, "DPP onProgress callback"); + Log.d(TAG, "Easy Connect onProgress callback"); mHandler.post(() -> { - mDppStatusCallback.onProgress(status); + mEasyConnectStatusCallback.onProgress(status); }); } } diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java index 6d82ca152202..f91790f5b3b1 100644 --- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java +++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java @@ -16,6 +16,7 @@ package android.net.wifi.hotspot2; +import android.annotation.SystemApi; import android.graphics.drawable.Icon; import android.net.Uri; import android.net.wifi.WifiSsid; @@ -36,16 +37,19 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class OsuProvider implements Parcelable { /** * OSU (Online Sign-Up) method: OMA DM (Open Mobile Alliance Device Management). * For more info, refer to Section 8.3 of the Hotspot 2.0 Release 2 Technical Specification. + * @hide */ public static final int METHOD_OMA_DM = 0; /** * OSU (Online Sign-Up) method: SOAP XML SPP (Subscription Provisioning Protocol). * For more info, refer to Section 8.4 of the Hotspot 2.0 Release 2 Technical Specification. + * @hide */ public static final int METHOD_SOAP_XML_SPP = 1; @@ -84,6 +88,7 @@ public final class OsuProvider implements Parcelable { */ private final Icon mIcon; + /** @hide */ public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames, String serviceDescription, Uri serverUri, String nai, List<Integer> methodList, Icon icon) { @@ -104,6 +109,7 @@ public final class OsuProvider implements Parcelable { * Copy constructor. * * @param source The source to copy from + * @hide */ public OsuProvider(OsuProvider source) { if (source == null) { @@ -130,10 +136,12 @@ public final class OsuProvider implements Parcelable { mIcon = source.mIcon; } + /** @hide */ public WifiSsid getOsuSsid() { return mOsuSsid; } + /** @hide */ public void setOsuSsid(WifiSsid osuSsid) { mOsuSsid = osuSsid; } @@ -162,10 +170,12 @@ public final class OsuProvider implements Parcelable { return mFriendlyNames.get(mFriendlyNames.keySet().stream().findFirst().get()); } + /** @hide */ public Map<String, String> getFriendlyNameList() { return mFriendlyNames; } + /** @hide */ public String getServiceDescription() { return mServiceDescription; } @@ -174,14 +184,17 @@ public final class OsuProvider implements Parcelable { return mServerUri; } + /** @hide */ public String getNetworkAccessIdentifier() { return mNetworkAccessIdentifier; } + /** @hide */ public List<Integer> getMethodList() { return mMethodList; } + /** @hide */ public Icon getIcon() { return mIcon; } diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java index a62d63cd8910..1ee874a9698b 100644 --- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java +++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java @@ -16,6 +16,7 @@ package android.net.wifi.hotspot2; +import android.annotation.SystemApi; import android.net.wifi.WifiManager; import android.os.Handler; @@ -25,6 +26,7 @@ import android.os.Handler; * * @hide */ +@SystemApi public abstract class ProvisioningCallback { /** diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index c744f18cf408..7bff68aaaa97 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -59,6 +59,7 @@ public class WifiConfigurationTest { WifiConfiguration config = new WifiConfiguration(); config.setPasspointManagementObjectTree(cookie); config.trusted = false; + config.updateIdentifier = "1234"; MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress(); Parcel parcelW = Parcel.obtain(); config.writeToParcel(parcelW, 0); @@ -73,6 +74,7 @@ public class WifiConfigurationTest { // lacking a useful config.equals, check two fields near the end. assertEquals(cookie, reconfig.getMoTree()); assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress()); + assertEquals(config.updateIdentifier, reconfig.updateIdentifier); assertFalse(reconfig.trusted); Parcel parcelWW = Parcel.obtain(); @@ -251,6 +253,18 @@ public class WifiConfigurationTest { } /** + * Verifies that updateIdentifier should be copied for copy constructor. + */ + @Test + public void testUpdateIdentifierForCopyConstructor() { + WifiConfiguration config = new WifiConfiguration(); + config.updateIdentifier = "1234"; + WifiConfiguration copyConfig = new WifiConfiguration(config); + + assertEquals(config.updateIdentifier, copyConfig.updateIdentifier); + } + + /** * Verifies that the serialization/de-serialization for softap config works. */ @Test |