diff options
448 files changed, 15323 insertions, 4165 deletions
diff --git a/Android.bp b/Android.bp index 834c9b08b07b..f032e6287d89 100644 --- a/Android.bp +++ b/Android.bp @@ -191,10 +191,6 @@ java_defaults { "core/java/android/hardware/input/IInputDevicesChangedListener.aidl", "core/java/android/hardware/input/ITabletModeChangedListener.aidl", "core/java/android/hardware/iris/IIrisService.aidl", - "core/java/android/hardware/location/IActivityRecognitionHardware.aidl", - "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl", - "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl", - "core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl", "core/java/android/hardware/location/IGeofenceHardware.aidl", "core/java/android/hardware/location/IGeofenceHardwareCallback.aidl", "core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl", @@ -358,6 +354,8 @@ java_defaults { "core/java/android/service/textclassifier/ITextLanguageCallback.aidl", "core/java/android/service/textclassifier/ITextLinksCallback.aidl", "core/java/android/service/textclassifier/ITextSelectionCallback.aidl", + "core/java/android/service/attention/IAttentionService.aidl", + "core/java/android/service/attention/IAttentionCallback.aidl", "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl", "core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl", "core/java/android/view/accessibility/IAccessibilityManager.aidl", @@ -889,7 +887,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", diff --git a/Android.mk b/Android.mk index e3cc2754fed3..9f7bf9905c09 100644 --- a/Android.mk +++ b/Android.mk @@ -80,6 +80,8 @@ update-api: doc-comment-check-docs # ==== hiddenapi lists ======================================= .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ + PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS) +$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ frameworks/base/config/hiddenapi-greylist.txt \ frameworks/base/config/hiddenapi-greylist-max-p.txt \ @@ -87,7 +89,8 @@ $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ frameworks/base/config/hiddenapi-force-blacklist.txt \ $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ - $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) + $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ + $(SOONG_HIDDENAPI_FLAGS) frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ diff --git a/api/current.txt b/api/current.txt index 6316ee8b0977..e7c9b24c05e7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6784,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"; @@ -6829,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"; @@ -6911,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 @@ -10367,6 +10375,7 @@ package android.content { field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; + field public static final java.lang.String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID"; field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT"; field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME"; field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY"; @@ -11942,6 +11951,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); } @@ -12282,6 +12294,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 { @@ -24014,6 +24027,7 @@ package android.media { method public android.view.Surface getSurface(); method public int getWidth(); method public static android.media.ImageReader newInstance(int, int, int, int); + method public static android.media.ImageReader newInstance(int, int, int, int, long); method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler); } @@ -24027,6 +24041,7 @@ package android.media { method public int getFormat(); method public int getMaxImages(); method public static android.media.ImageWriter newInstance(android.view.Surface, int); + method public static android.media.ImageWriter newInstance(android.view.Surface, int, int); method public void queueInputImage(android.media.Image); method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler); } @@ -24138,6 +24153,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(); @@ -24264,10 +24280,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 { @@ -24343,7 +24364,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"; @@ -24365,6 +24389,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 @@ -24382,6 +24433,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 @@ -24544,12 +24598,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); @@ -24566,6 +24661,7 @@ package android.media { ctor public MediaController2(android.content.Context, android.media.Session2Token, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback); method public void cancelSessionCommand(java.lang.Object); method public void close(); + method public boolean isPlaybackActive(); method public java.lang.Object sendSessionCommand(android.media.Session2Command, android.os.Bundle); } @@ -24574,6 +24670,7 @@ package android.media { method public void onCommandResult(android.media.MediaController2, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result); method public void onConnected(android.media.MediaController2, android.media.Session2CommandGroup); method public void onDisconnected(android.media.MediaController2); + method public void onPlaybackActiveChanged(android.media.MediaController2, boolean); method public android.media.Session2Command.Result onSessionCommand(android.media.MediaController2, android.media.Session2Command, android.os.Bundle); } @@ -25002,6 +25099,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"; @@ -25915,7 +26013,9 @@ package android.media { method public void close(); method public java.lang.String getSessionId(); method public android.media.Session2Token getSessionToken(); + method public boolean isPlaybackActive(); method public java.lang.Object sendSessionCommand(android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle); + method public void setPlaybackActive(boolean); } public static final class MediaSession2.Builder { @@ -29823,7 +29923,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(); @@ -29843,7 +29943,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); @@ -48443,6 +48543,7 @@ package android.view { field public static final android.os.Parcelable.Creator<android.view.Display.HdrCapabilities> CREATOR; field public static final int HDR_TYPE_DOLBY_VISION = 1; // 0x1 field public static final int HDR_TYPE_HDR10 = 2; // 0x2 + field public static final int HDR_TYPE_HDR10_PLUS = 4; // 0x4 field public static final int HDR_TYPE_HLG = 3; // 0x3 field public static final float INVALID_LUMINANCE = -1.0f; } @@ -52581,7 +52682,7 @@ package android.view.contentcapture { method public final void notifyViewAppeared(android.view.ViewStructure); method public final void notifyViewDisappeared(android.view.autofill.AutofillId); method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int); - field public static final int FLAG_USER_INPUT = 1; // 0x1 + method public final void notifyViewsDisappeared(android.view.autofill.AutofillId, int[]); } public final class ContentCaptureSessionId implements android.os.Parcelable { @@ -54245,6 +54346,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(); @@ -54294,6 +54397,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>); @@ -54409,6 +54514,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 acbc2a72241f..e9743f7c0d43 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -18,6 +18,7 @@ package android { field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER"; field public static final java.lang.String BACKUP = "android.permission.BACKUP"; + field public static final java.lang.String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE"; field public static final java.lang.String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE"; field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE"; field public static final java.lang.String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE"; @@ -128,6 +129,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"; @@ -184,6 +186,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 +198,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 } @@ -1332,7 +1336,7 @@ package android.content.pm { } public class CrossProfileApps { - method public void startAnyActivity(android.content.ComponentName, android.os.UserHandle); + method public void startActivity(android.content.ComponentName, android.os.UserHandle); } public final class InstantAppInfo implements android.os.Parcelable { @@ -1559,6 +1563,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 @@ -1567,6 +1572,18 @@ package android.content.pm { field public int requestRes; } + public class ShortcutManager { + method public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(android.content.IntentFilter); + } + + public static final class ShortcutManager.ShareShortcutInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.content.pm.ShortcutInfo getShortcutInfo(); + method public android.content.ComponentName getTargetComponent(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutManager.ShareShortcutInfo> CREATOR; + } + public final class SuspendDialogInfo implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -1754,6 +1771,7 @@ package android.hardware.display { } public final class ColorDisplayManager { + method public boolean setAppSaturationLevel(java.lang.String, int); method public boolean setSaturationLevel(int); } @@ -2883,6 +2901,7 @@ package android.location { method public int getConstellationType(); method public float getExcessPathLengthMeters(); method public float getExcessPathLengthUncertaintyMeters(); + method public float getProbSatIsLos(); method public android.location.GnssReflectingPlane getReflectingPlane(); method public int getSatId(); method public int getSingleSatCorrectionFlags(); @@ -2890,13 +2909,12 @@ package android.location { method public boolean hasExcessPathLengthUncertainty(); method public boolean hasReflectingPlane(); method public boolean hasSatelliteLineOfSight(); - method public boolean isSatelliteLineOfSight(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR; field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2 field public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 4; // 0x4 + field public static final int HAS_PROB_SAT_IS_LOS_MASK = 1; // 0x1 field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8 - field public static final int HAS_SAT_IS_LOS_MASK = 1; // 0x1 } public static class GnssSingleSatCorrection.Builder { @@ -2906,9 +2924,9 @@ package android.location { method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int); method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float); method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float); + method public android.location.GnssSingleSatCorrection.Builder setProbSatIsLos(float); method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane); method public android.location.GnssSingleSatCorrection.Builder setSatId(int); - method public android.location.GnssSingleSatCorrection.Builder setSatIsLos(boolean); method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int); } @@ -3915,10 +3933,10 @@ package android.net.wifi { method public abstract void onEnrolleeSuccess(int); method public abstract void onFailure(int); method public abstract void onProgress(int); - 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_GENERIC = -7; // 0xfffffff9 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 @@ -4140,12 +4158,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(); @@ -4160,6 +4185,7 @@ package android.net.wifi { 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 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 @@ -4179,6 +4205,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 @@ -4355,6 +4383,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 { @@ -5025,6 +5106,9 @@ 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_AUTOFILL = "autofill"; + field public static final java.lang.String NAMESPACE_CONTENT_CAPTURE = "content_capture"; + 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"; } @@ -5340,7 +5424,8 @@ package android.security.keystore.recovery { public class RecoveryController { method public android.security.keystore.recovery.RecoverySession createRecoverySession(); - method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; + method public deprecated java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; + method public java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException; method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context); method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException; @@ -5348,7 +5433,8 @@ package android.security.keystore.recovery { method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException; method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; method public java.util.Map<java.lang.String, java.security.cert.X509Certificate> getRootCertificates(); - method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; + method public deprecated java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; + method public java.security.Key importKey(java.lang.String, byte[], byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException; method public static boolean isRecoverableKeyStoreEnabled(android.content.Context); method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; @@ -5375,6 +5461,7 @@ package android.security.keystore.recovery { method public int describeContents(); method public java.lang.String getAlias(); method public byte[] getEncryptedKeyMaterial(); + method public byte[] getMetadata(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.WrappedApplicationKey> CREATOR; } @@ -5384,6 +5471,7 @@ package android.security.keystore.recovery { method public android.security.keystore.recovery.WrappedApplicationKey build(); method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String); method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]); + method public android.security.keystore.recovery.WrappedApplicationKey.Builder setMetadata(byte[]); } } @@ -5406,6 +5494,28 @@ package android.service.appprediction { } +package android.service.attention { + + public abstract class AttentionService extends android.app.Service { + ctor public AttentionService(); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract void onCancelAttentionCheck(int); + method public abstract void onCheckAttention(int, android.service.attention.AttentionService.AttentionCallback); + field public static final int ATTENTION_FAILURE_PREEMPTED = 2; // 0x2 + field public static final int ATTENTION_FAILURE_TIMED_OUT = 3; // 0x3 + field public static final int ATTENTION_FAILURE_UNKNOWN = 4; // 0x4 + field public static final int ATTENTION_SUCCESS_ABSENT = 0; // 0x0 + field public static final int ATTENTION_SUCCESS_PRESENT = 1; // 0x1 + field public static final java.lang.String SERVICE_INTERFACE = "android.service.attention.AttentionService"; + } + + public static final class AttentionService.AttentionCallback { + method public void onFailure(int, int); + method public void onSuccess(int, int, long); + } + +} + package android.service.autofill { public abstract class AutofillFieldClassificationService extends android.app.Service { @@ -5636,6 +5746,7 @@ package android.service.euicc { field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; field public static final java.lang.String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES"; field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; + field public static final java.lang.String EXTRA_RESOLUTION_CARD_ID = "android.service.euicc.extra.RESOLUTION_CARD_ID"; field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT"; @@ -6267,6 +6378,30 @@ package android.telephony { field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string"; } + public final class CarrierRestrictionRules implements android.os.Parcelable { + method public int describeContents(); + method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(); + method public int getDefaultCarrierRestriction(); + method public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers(); + method public int getMultiSimPolicy(); + method public boolean isAllCarriersAllowed(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1 + field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0 + field public static final android.os.Parcelable.Creator<android.telephony.CarrierRestrictionRules> CREATOR; + field public static final int MULTISIM_POLICY_NONE = 0; // 0x0 + field public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1; // 0x1 + } + + public static class CarrierRestrictionRules.Builder { + method public android.telephony.CarrierRestrictionRules build(); + method public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed(); + method public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(java.util.List<android.service.carrier.CarrierIdentifier>); + method public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int); + method public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(java.util.List<android.service.carrier.CarrierIdentifier>); + method public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int); + } + public final class DataFailCause { field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f @@ -6760,12 +6895,14 @@ package android.telephony { method public void dial(java.lang.String); method public boolean disableDataConnectivity(); method public boolean enableDataConnectivity(); + method public boolean enableModemForSlot(int, boolean); method public void enableVideoCalling(boolean); method public java.lang.String getAidForAppType(int); - method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int); + method public deprecated java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int); method public int getCardIdForDefaultEuicc(); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); + method public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules(); method public java.lang.String getCdmaMdn(); method public java.lang.String getCdmaMdn(int); method public java.lang.String getCdmaMin(); @@ -6804,8 +6941,9 @@ package android.telephony { method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback); method public void requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback); method public boolean resetRadioConfig(); - method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); + method public deprecated int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method public void setCarrierDataEnabled(boolean); + method public int setCarrierRestrictionRules(android.telephony.CarrierRestrictionRules); method public void setDataActivationState(int); method public deprecated void setDataEnabled(int, boolean); method public void setDataRoamingEnabled(boolean); @@ -6857,6 +6995,9 @@ package android.telephony { field public static final int RADIO_POWER_OFF = 0; // 0x0 field public static final int RADIO_POWER_ON = 1; // 0x1 field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2 + field public static final int SET_CARRIER_RESTRICTION_ERROR = 2; // 0x2 + field public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; // 0x1 + field public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0; // 0x0 field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2 field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1 field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3 @@ -7187,6 +7328,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); @@ -7195,6 +7337,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); @@ -8195,8 +8338,8 @@ package android.view.contentcapture { public final class ContentCaptureEvent implements android.os.Parcelable { method public int describeContents(); method public long getEventTime(); - method public int getFlags(); method public android.view.autofill.AutofillId getId(); + method public java.util.List<android.view.autofill.AutofillId> getIds(); method public java.lang.CharSequence getText(); method public int getType(); method public android.view.contentcapture.ViewNode getViewNode(); @@ -8500,6 +8643,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); @@ -8549,6 +8694,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/test-current.txt b/api/test-current.txt index 6ddb341191cb..08531829cba1 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 @@ -510,6 +511,8 @@ package android.graphics { } public class Paint { + method public long getColorLong(); + method public long getShadowLayerColorLong(); method public void setColor(long); method public void setShadowLayer(float, float, float, long); } @@ -579,6 +582,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; @@ -586,10 +591,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(); @@ -1103,6 +1120,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); @@ -2089,6 +2124,33 @@ package android.view.inputmethod { } +package android.view.inspector { + + public abstract class InspectableNodeName implements java.lang.annotation.Annotation { + } + + public abstract class InspectableProperty implements java.lang.annotation.Annotation { + } + + public static abstract class InspectableProperty.EnumMap implements java.lang.annotation.Annotation { + } + + public static abstract class InspectableProperty.FlagMap implements java.lang.annotation.Annotation { + } + + public static final class InspectableProperty.ValueType extends java.lang.Enum { + method public static android.view.inspector.InspectableProperty.ValueType valueOf(java.lang.String); + method public static final android.view.inspector.InspectableProperty.ValueType[] values(); + enum_constant public static final android.view.inspector.InspectableProperty.ValueType COLOR; + enum_constant public static final android.view.inspector.InspectableProperty.ValueType GRAVITY; + enum_constant public static final android.view.inspector.InspectableProperty.ValueType INFERRED; + enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_ENUM; + enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_FLAG; + enum_constant public static final android.view.inspector.InspectableProperty.ValueType NONE; + } + +} + package android.widget { public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener { 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 7e2df275515e..ef3eac017192 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -188,6 +188,11 @@ message Atom { BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125; BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126; BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127; + AppDowngraded app_downgraded = 128; + AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129; + LowStorageStateChanged low_storage_state_changed = 130; + GnssNfwNotificationReported gnss_nfw_notification_reported = 131; + GnssConfigurationReported gnss_configuration_reported = 132; } // Pulled events will start at field 10000. @@ -1787,6 +1792,47 @@ message ActivityForegroundStateChanged { } /** + * Logs when a volume entered low Storage state. + * Logged from: + * frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java + */ +message LowStorageStateChanged { + // Volume that ran out of storage. + optional string volume_description = 1; + + enum State { + UNKNOWN = 0; + OFF = 1; + ON = 2; + } + optional State state = 2; +} + +/** + * Logs when an app is downgraded. + * Logged from: + * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java + */ +message AppDowngraded { + optional string package_name = 1; + // Size of the package (all data) before being downgraded. + optional int64 size_in_bytes_before = 2; + // Size of the package (all data) after being downgraded. + optional int64 size_in_bytes_after = 3; + + optional bool aggressive = 4; +} + +/** + * Logs when an app is optimized after being downgraded. + * Logged from: + * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java + */ +message AppOptimizedAfterDowngraded { + optional string package_name = 1; +} + +/** * Logs when an app crashes. * Logged from: * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3331,7 +3377,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; @@ -3410,6 +3457,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; @@ -3417,6 +3467,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 */ @@ -4069,7 +4134,7 @@ message GnssNiEventReported { 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; + optional android.server.location.GnssNiType ni_type = 3; // NI requires notification. optional bool need_notify = 4; @@ -4085,7 +4150,7 @@ message GnssNiEventReported { optional int32 timeout = 7; // Default response when timeout. - optional int32 default_response = 8; + optional android.server.location.GnssUserResponseType default_response = 8; // String representing the requester of the network inititated location request. optional string requestor_id = 9; @@ -4095,10 +4160,10 @@ message GnssNiEventReported { optional string text = 10; // requestorId decoding scheme. - optional int32 requestor_id_encoding = 11; + optional android.server.location.GnssNiEncodingType requestor_id_encoding = 11; // Notification message text decoding scheme. - optional int32 text_encoding = 12; + optional android.server.location.GnssNiEncodingType text_encoding = 12; // True if SUPL ES is enabled. optional bool is_supl_es_enabled = 13; @@ -4107,5 +4172,94 @@ message GnssNiEventReported { optional bool is_location_enabled = 14; // GNSS NI responses which define the response in NI structures. - optional int32 user_response = 15; + optional android.server.location.GnssUserResponseType user_response = 15; +} + +/** + * Logs GNSS non-framework (NFW) location notification. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java + */ +message GnssNfwNotificationReported { + // Package name of the Android proxy application representing the non-framework entity that + // requested location. Set to empty string if unknown. + optional string proxy_app_package_name = 1; + + // Protocol stack that initiated the non-framework location request. + optional android.server.location.NfwProtocolStack protocol_stack = 2; + + // Name of the protocol stack if protocol_stack field is set to OTHER_PROTOCOL_STACK. Otherwise, + // set to empty string. This field is opaque to the framework and used for logging purposes. + optional string other_protocol_stack_name = 3; + + // Source initiating/receiving the location information. + optional android.server.location.NfwRequestor requestor = 4; + + // Identity of the endpoint receiving the location information. For example, carrier name, OEM + // name, SUPL SLP/E-SLP FQDN, chipset vendor name, etc. This field is opaque to the framework + // and used for logging purposes. + optional string requestor_id = 5; + + // Indicates whether location information was provided for this request. + optional android.server.location.NfwResponseType response_type = 6; + + // True if the device is in user initiated emergency session. + optional bool in_emergency_mode = 7; + + // True if cached location is provided. + optional bool is_cached_location = 8; + + // True if proxy app permission mismatch between framework and GNSS HAL. + optional bool is_permission_mismatched = 9; +} + +/** + * Logs GNSS configuration as defined in IGnssConfiguration.hal. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/location/GnssConfiguration.java + */ +message GnssConfigurationReported { + // SUPL host name. + optional string supl_host = 1; + + // SUPL port number. + optional int32 supl_port = 2; + + // C2K host name. + optional string c2k_host = 3; + + // C2K port number. + optional int32 c2k_port = 4; + + // The SUPL version requested by Carrier. + optional int32 supl_ver = 5; + + // The SUPL mode. + optional android.server.location.SuplMode supl_mode = 6; + + // True if NI emergency SUPL restrictions is enabled. + optional bool supl_es = 7; + + // LTE Positioning Profile settings + optional android.server.location.LppProfile lpp_profile = 8; + + // Positioning protocol on A-Glonass system. + optional android.server.location.GlonassPosProtocol a_glonass_pos_protocol_select = 9; + + // True if emergency PDN is used. Otherwise, regular PDN is used. + optional bool use_emergency_pdn_for_emergency_supl= 10; + + // Configurations of how GPS functionalities should be locked when user turns off GPS On setting. + optional android.server.location.GpsLock gps_lock = 11; + + // Number of seconds to extend the emergency session duration post emergency call. + optional int32 es_extension_sec = 12; + + // The full list of package names of proxy Android applications representing the non-framework + // location access entities (on/off the device) for which the framework user has granted + // non-framework location access permission. The package names are concatenated in one string + // with spaces as separators. + optional string enabled_proxy_app_package_name_list = 13; } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 67a1a4720576..c2878f02a691 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -93,7 +93,8 @@ GaugeMetricProducer::GaugeMetricProducer( StatsdStats::kAtomDimensionKeySizeLimitMap.end() ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second : StatsdStats::kDimensionKeySizeHardLimit), - mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()) { + mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()), + mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) { mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>(); mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>(); int64_t bucketSizeMills = 0; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index a1a506144d43..df0877954d70 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -74,6 +74,9 @@ public: const int64_t version) override { std::lock_guard<std::mutex> lock(mMutex); + if (!mSplitBucketForAppUpgrade) { + return; + } if (eventTimeNs > getCurrentBucketEndTimeNs()) { // Flush full buckets on the normal path up to the latest bucket boundary. flushIfNeededLocked(eventTimeNs); @@ -176,11 +179,14 @@ private: const size_t mGaugeAtomsPerDimensionLimit; + const bool mSplitBucketForAppUpgrade; + FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade); FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade); + FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection); FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 9a8e3bd7b3eb..4122d8440959 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -105,7 +105,8 @@ ValueMetricProducer::ValueMetricProducer( mUseZeroDefaultBase(metric.use_zero_default_base()), mHasGlobalBase(false), mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC - : StatsdStats::kPullMaxDelayNs) { + : StatsdStats::kPullMaxDelayNs), + mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) { int64_t bucketSizeMills = 0; if (metric.has_bucket()) { bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 4865aee57187..69eb0afaf7c4 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -57,6 +57,9 @@ public: void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, const int64_t version) override { std::lock_guard<std::mutex> lock(mMutex); + if (!mSplitBucketForAppUpgrade) { + return; + } if (mIsPulled && mCondition) { pullAndMatchEventsLocked(eventTimeNs - 1); } @@ -185,6 +188,8 @@ private: const int64_t mMaxPullDelayNs; + const bool mSplitBucketForAppUpgrade; + FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); @@ -193,6 +198,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse); + FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 381ac321a77a..9d3a66902804 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -244,6 +244,8 @@ message GaugeMetric { optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10]; optional int32 max_pull_delay_sec = 13 [default = 10]; + + optional bool split_bucket_for_app_upgrade = 14 [default = true]; } message ValueMetric { @@ -290,6 +292,8 @@ message ValueMetric { optional bool skip_zero_diff_output = 14 [default = true]; optional int32 max_pull_delay_sec = 16 [default = 10]; + + optional bool split_bucket_for_app_upgrade = 17 [default = true]; } message Alert { diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 2799107c53d2..0ffbb54c8d48 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -276,8 +276,9 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { UidMap uidMap; SimpleAtomMatcher atomMatcher; atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ - new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); @@ -295,9 +296,8 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, - tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); vector<shared_ptr<LogEvent>> allData; shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); @@ -337,6 +337,58 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { ->mValue.int_value); } +TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + metric.set_split_bucket_for_app_upgrade(false); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, + tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); + + vector<shared_ptr<LogEvent>> allData; + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); + event->write("some value"); + event->write(1); + event->init(); + allData.push_back(event); + gaugeProducer.onDataPulled(allData); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); +} + TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { GaugeMetric metric; metric.set_id(metricId); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 67570fc2ab51..9cfe3436ada7 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -641,7 +641,8 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); + EXPECT_EQ(20L, + valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -652,7 +653,48 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { valueProducer.onDataPulled(allData); EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); + EXPECT_EQ(20L, + valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); +} + +TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_max_pull_delay_sec(INT_MAX); + metric.set_split_bucket_for_app_upgrade(false); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true)); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(tagId); + event->write(100); + event->init(); + allData.push_back(event); + + valueProducer.onDataPulled(allData); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + + valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(bucketStartTimeNs, valueProducer.mCurrentBucketStartTimeNs); } TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index c2e441b35f48..3ec0db4d99ce 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -2768,11 +2768,6 @@ HPLandroid/hardware/location/GeofenceHardwareService$1;->getStatusOfMonitoringTy HPLandroid/hardware/location/GeofenceHardwareService$1;->registerForMonitorStateChangeCallback(ILandroid/hardware/location/IGeofenceHardwareMonitorCallback;)Z HPLandroid/hardware/location/GeofenceHardwareService$1;->removeGeofence(II)Z HPLandroid/hardware/location/GeofenceHardwareService;->checkPermission(III)V -HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub$Proxy;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V -HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V -HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IActivityRecognitionHardwareClient; -HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z -HPLandroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V HPLandroid/hardware/location/IContextHubCallback$Stub;->asBinder()Landroid/os/IBinder; HPLandroid/hardware/location/IContextHubCallback$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z HPLandroid/hardware/location/IContextHubService$Stub$Proxy;->findNanoAppOnHub(ILandroid/hardware/location/NanoAppFilter;)[I @@ -21782,7 +21777,6 @@ HSPLandroid/hardware/input/KeyboardLayout$1;-><init>()V HSPLandroid/hardware/input/TouchCalibration$1;-><init>()V HSPLandroid/hardware/input/TouchCalibration;-><init>()V HSPLandroid/hardware/input/TouchCalibration;->getAffineTransform()[F -HSPLandroid/hardware/location/ActivityRecognitionHardware;->isSupported()Z HSPLandroid/hardware/location/ContextHubInfo$1;-><init>()V HSPLandroid/hardware/location/ContextHubInfo;-><init>(Landroid/hardware/contexthub/V1_0/ContextHub;)V HSPLandroid/hardware/location/ContextHubMessage$1;-><init>()V @@ -21802,13 +21796,6 @@ HSPLandroid/hardware/location/GeofenceHardwareService$1;->setGpsGeofenceHardware HSPLandroid/hardware/location/GeofenceHardwareService;-><init>()V HSPLandroid/hardware/location/GeofenceHardwareService;->onBind(Landroid/content/Intent;)Landroid/os/IBinder; HSPLandroid/hardware/location/GeofenceHardwareService;->onCreate()V -HSPLandroid/hardware/location/IActivityRecognitionHardware;->disableActivityEvent(Ljava/lang/String;I)Z -HSPLandroid/hardware/location/IActivityRecognitionHardware;->enableActivityEvent(Ljava/lang/String;IJ)Z -HSPLandroid/hardware/location/IActivityRecognitionHardware;->flush()Z -HSPLandroid/hardware/location/IActivityRecognitionHardware;->getSupportedActivities()[Ljava/lang/String; -HSPLandroid/hardware/location/IActivityRecognitionHardware;->isActivitySupported(Ljava/lang/String;)Z -HSPLandroid/hardware/location/IActivityRecognitionHardware;->registerSink(Landroid/hardware/location/IActivityRecognitionHardwareSink;)Z -HSPLandroid/hardware/location/IActivityRecognitionHardware;->unregisterSink(Landroid/hardware/location/IActivityRecognitionHardwareSink;)Z HSPLandroid/hardware/location/IContextHubCallback$Stub$Proxy;->asBinder()Landroid/os/IBinder; HSPLandroid/hardware/location/IContextHubCallback$Stub$Proxy;->onMessageReceipt(IILandroid/hardware/location/ContextHubMessage;)V HSPLandroid/hardware/location/IContextHubCallback;->onMessageReceipt(IILandroid/hardware/location/ContextHubMessage;)V @@ -55650,7 +55637,6 @@ Landroid/hardware/input/InputManagerInternal; Landroid/hardware/input/KeyboardLayout$1; Landroid/hardware/input/TouchCalibration$1; Landroid/hardware/input/TouchCalibration; -Landroid/hardware/location/ActivityRecognitionHardware; Landroid/hardware/location/ContextHubInfo$1; Landroid/hardware/location/ContextHubInfo; Landroid/hardware/location/ContextHubManager; @@ -55666,11 +55652,6 @@ Landroid/hardware/location/GeofenceHardwareMonitorEvent; Landroid/hardware/location/GeofenceHardwareRequestParcelable$1; Landroid/hardware/location/GeofenceHardwareService$1; Landroid/hardware/location/GeofenceHardwareService; -Landroid/hardware/location/IActivityRecognitionHardware$Stub; -Landroid/hardware/location/IActivityRecognitionHardware; -Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub$Proxy; -Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub; -Landroid/hardware/location/IActivityRecognitionHardwareClient; Landroid/hardware/location/IContextHubCallback$Stub$Proxy; Landroid/hardware/location/IContextHubCallback; Landroid/hardware/location/IContextHubClient$Stub; diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 5392a3c428c1..13f8dd924d4a 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -462,8 +462,6 @@ Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager; Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z -Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V -Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService; Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager; @@ -1108,18 +1106,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 @@ -1824,22 +1810,10 @@ Lcom/android/internal/os/RuntimeInit;->getApplicationObject()Landroid/os/IBinder Lcom/android/internal/os/RuntimeInit;->initialized:Z Lcom/android/internal/os/RuntimeInit;->main([Ljava/lang/String;)V Lcom/android/internal/os/RuntimeInit;->mApplicationObject:Landroid/os/IBinder; -Lcom/android/internal/os/ZygoteConnection$Arguments;-><init>([Ljava/lang/String;)V -Lcom/android/internal/os/ZygoteConnection$Arguments;->effectiveCapabilities:J -Lcom/android/internal/os/ZygoteConnection$Arguments;->gid:I -Lcom/android/internal/os/ZygoteConnection$Arguments;->gids:[I -Lcom/android/internal/os/ZygoteConnection$Arguments;->permittedCapabilities:J -Lcom/android/internal/os/ZygoteConnection$Arguments;->remainingArgs:[Ljava/lang/String; -Lcom/android/internal/os/ZygoteConnection$Arguments;->rlimits:Ljava/util/ArrayList; -Lcom/android/internal/os/ZygoteConnection$Arguments;->uid:I -Lcom/android/internal/os/ZygoteConnection;->applyUidSecurityPolicy(Lcom/android/internal/os/ZygoteConnection$Arguments;Landroid/net/Credentials;)V Lcom/android/internal/os/ZygoteConnection;->closeSocket()V -Lcom/android/internal/os/ZygoteConnection;->getFileDesciptor()Ljava/io/FileDescriptor; -Lcom/android/internal/os/ZygoteConnection;->intArray2d:[[I Lcom/android/internal/os/ZygoteConnection;->mSocket:Landroid/net/LocalSocket; Lcom/android/internal/os/ZygoteConnection;->mSocketOutStream:Ljava/io/DataOutputStream; Lcom/android/internal/os/ZygoteConnection;->peer:Landroid/net/Credentials; -Lcom/android/internal/os/ZygoteConnection;->readArgumentList()[Ljava/lang/String; Lcom/android/internal/os/ZygoteInit;->main([Ljava/lang/String;)V Lcom/android/internal/os/ZygoteInit;->mResources:Landroid/content/res/Resources; Lcom/android/internal/os/ZygoteSecurityException;-><init>(Ljava/lang/String;)V diff --git a/config/preloaded-classes b/config/preloaded-classes index c8a2a9c19b28..cd798adf5d44 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -1405,10 +1405,7 @@ android.hardware.input.InputManager android.hardware.input.InputManager$InputDeviceListener android.hardware.input.InputManager$InputDeviceListenerDelegate android.hardware.input.InputManager$InputDevicesChangedListener -android.hardware.location.ActivityRecognitionHardware android.hardware.location.ContextHubManager -android.hardware.location.IActivityRecognitionHardware -android.hardware.location.IActivityRecognitionHardware$Stub android.hardware.radio.RadioManager android.hardware.soundtrigger.SoundTrigger android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d374f1c0acdf..836627efb379 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -124,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; @@ -1034,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) @@ -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/AlertDialog.java b/core/java/android/app/AlertDialog.java index cf40e067e5b1..bfc216a24c1b 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -25,6 +25,7 @@ import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.DialogInterface; import android.content.res.ResourceId; +import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -465,7 +466,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * @param context the parent context */ public Builder(Context context) { - this(context, resolveDialogTheme(context, ResourceId.ID_NULL)); + this(context, resolveDialogTheme(context, Resources.ID_NULL)); } /** diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 4bd935c0e924..088c245c9c2c 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -31,7 +31,7 @@ import android.content.ContextWrapper; import android.content.DialogInterface; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; -import android.content.res.ResourceId; +import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; @@ -183,7 +183,7 @@ public class Dialog implements DialogInterface, Window.Callback, Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { - if (themeResId == ResourceId.ID_NULL) { + if (themeResId == Resources.ID_NULL) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); themeResId = outValue.resourceId; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 72819cb1493b..b8d748dce9e6 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -384,9 +384,7 @@ public class Notification implements Parcelable STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging); STANDARD_LAYOUTS.add(R.layout.notification_template_material_media); STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media); - STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header); STANDARD_LAYOUTS.add(R.layout.notification_template_header); - STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient); } /** @@ -4570,9 +4568,7 @@ public class Notification implements Parcelable if (p.title != null) { contentView.setViewVisibility(R.id.title, View.VISIBLE); contentView.setTextViewText(R.id.title, processTextSpans(p.title)); - if (!p.ambient) { - setTextViewColorPrimary(contentView, R.id.title, p); - } + setTextViewColorPrimary(contentView, R.id.title, p); contentView.setViewLayoutWidth(R.id.title, showProgress ? ViewGroup.LayoutParams.WRAP_CONTENT : ViewGroup.LayoutParams.MATCH_PARENT); @@ -4581,9 +4577,7 @@ public class Notification implements Parcelable int textId = showProgress ? com.android.internal.R.id.text_line_1 : com.android.internal.R.id.text; contentView.setTextViewText(textId, processTextSpans(p.text)); - if (!p.ambient) { - setTextViewColorSecondary(contentView, textId, p); - } + setTextViewColorSecondary(contentView, textId, p); contentView.setViewVisibility(textId, View.VISIBLE); } @@ -4842,7 +4836,7 @@ public class Notification implements Parcelable if (mN.mLargeIcon == null && mN.largeIcon != null) { mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon); } - boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient; + boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon; if (showLargeIcon) { contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon); @@ -4856,7 +4850,7 @@ public class Notification implements Parcelable * @return if the reply icon is visible */ private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) { - boolean actionVisible = !p.hideReplyIcon && !p.ambient; + boolean actionVisible = !p.hideReplyIcon; Action action = null; if (actionVisible) { action = findReplyAction(); @@ -4896,21 +4890,18 @@ public class Notification implements Parcelable private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) { bindSmallIcon(contentView, p); bindHeaderAppName(contentView, p); - if (!p.ambient) { - // Ambient view does not have these - bindHeaderText(contentView, p); - bindHeaderTextSecondary(contentView, p); - bindHeaderChronometerAndTime(contentView, p); - bindProfileBadge(contentView, p); - bindAlertedIcon(contentView, p); - } + bindHeaderText(contentView, p); + bindHeaderTextSecondary(contentView, p); + bindHeaderChronometerAndTime(contentView, p); + bindProfileBadge(contentView, p); + bindAlertedIcon(contentView, p); bindActivePermissions(contentView, p); bindExpandButton(contentView, p); mN.mUsesStandardHeader = true; } private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) { - int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p); + int color = getNeutralColor(p); contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP); contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP); contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP); @@ -5021,13 +5012,12 @@ public class Notification implements Parcelable if (isColorized(p)) { setTextViewColorPrimary(contentView, R.id.app_name_text, p); } else { - contentView.setTextColor(R.id.app_name_text, - p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p)); + contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p)); } } private boolean isColorized(StandardTemplateParams p) { - return p.allowColorization && !p.ambient && mN.isColorized(); + return p.allowColorization && mN.isColorized(); } private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) { @@ -5097,7 +5087,7 @@ public class Notification implements Parcelable List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions); int N = nonContextualActions.size(); - boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient; + boolean emphazisedMode = mN.fullScreenIntent != null; big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode); if (N > 0) { big.setViewVisibility(R.id.actions_container, View.VISIBLE); @@ -5122,7 +5112,7 @@ public class Notification implements Parcelable } CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY); - if (!p.ambient && validRemoteInput && replyText != null + if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0]) && p.maxRemoteInputHistory > 0) { boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER); @@ -5239,11 +5229,10 @@ public class Notification implements Parcelable * Construct a RemoteViews for the final notification header only. This will not be * colorized. * - * @param ambient if true, generate the header for the ambient display layout. * @hide */ - public RemoteViews makeNotificationHeader(boolean ambient) { - return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this)); + public RemoteViews makeNotificationHeader() { + return makeNotificationHeader(mParams.reset().fillTextsFrom(this)); } /** @@ -5256,8 +5245,7 @@ public class Notification implements Parcelable // Headers on their own are never colorized p.disallowColorization(); RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(), - p.ambient ? R.layout.notification_template_ambient_header - : R.layout.notification_template_header); + R.layout.notification_template_header); resetNotificationHeader(header); bindNotificationHeader(header, p); return header; @@ -5269,11 +5257,7 @@ public class Notification implements Parcelable * @hide */ public RemoteViews makeAmbientNotification() { - RemoteViews ambient = applyStandardTemplateWithActions( - R.layout.notification_template_material_ambient, - mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false), - null /* result */); - return ambient; + return createHeadsUpContentView(false /* increasedHeight */); } private void hideLine1Text(RemoteViews result) { @@ -5377,14 +5361,8 @@ public class Notification implements Parcelable } mN.extras = publicExtras; RemoteViews view; - if (ambient) { - publicExtras.putCharSequence(EXTRA_TITLE, - mContext.getString(com.android.internal.R.string.notification_hidden_text)); - view = makeAmbientNotification(); - } else{ - view = makeNotificationHeader(false /* ambient */); - view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true); - } + view = makeNotificationHeader(); + view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true); mN.extras = savedBundle; mN.mLargeIcon = largeIcon; mN.largeIcon = largeIconLegacy; @@ -5404,7 +5382,6 @@ public class Notification implements Parcelable public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) { StandardTemplateParams p = mParams.reset() .forceDefaultColor() - .ambient(false) .fillTextsFrom(this); if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) { p.summaryText(createSummaryText()); @@ -5495,8 +5472,7 @@ public class Notification implements Parcelable if (isColorized(p)) { setTextViewColorPrimary(button, R.id.action0, p); } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) { - button.setTextColor(R.id.action0, - p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p)); + button.setTextColor(R.id.action0, resolveContrastColor(p)); } } button.setIntTag(R.id.action0, R.id.notification_action_index_tag, @@ -5589,13 +5565,8 @@ public class Notification implements Parcelable } private CharSequence processLegacyText(CharSequence charSequence) { - return processLegacyText(charSequence, false /* ambient */); - } - - private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) { boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion(); - boolean wantLightText = ambient; - if (isAlreadyLightText != wantLightText) { + if (isAlreadyLightText) { return getColorUtil().invertCharSequenceColors(charSequence); } else { return charSequence; @@ -5609,9 +5580,7 @@ public class Notification implements Parcelable StandardTemplateParams p) { boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon); int color; - if (p.ambient) { - color = resolveAmbientColor(p); - } else if (isColorized(p)) { + if (isColorized(p)) { color = getPrimaryTextColor(p); } else { color = resolveContrastColor(p); @@ -5698,17 +5667,6 @@ public class Notification implements Parcelable return mNeutralColor; } - int resolveAmbientColor(StandardTemplateParams p) { - int rawColor = getRawColor(p); - if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) { - return mCachedAmbientColor; - } - final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor); - - mCachedAmbientColorIsFor = rawColor; - return mCachedAmbientColor = contrasted; - } - /** * Apply the unstyled operations and return a new {@link Notification} object. * @hide @@ -10144,7 +10102,6 @@ public class Notification implements Parcelable private static class StandardTemplateParams { boolean hasProgress = true; - boolean ambient = false; CharSequence title; CharSequence text; CharSequence headerTextSecondary; @@ -10157,7 +10114,6 @@ public class Notification implements Parcelable final StandardTemplateParams reset() { hasProgress = true; - ambient = false; title = null; text = null; summaryText = null; @@ -10213,22 +10169,15 @@ public class Notification implements Parcelable return this; } - final StandardTemplateParams ambient(boolean ambient) { - Preconditions.checkState(title == null && text == null, "must set ambient before text"); - this.ambient = ambient; - return this; - } - final StandardTemplateParams fillTextsFrom(Builder b) { Bundle extras = b.mN.extras; - this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient); + this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE)); - // Big text notifications should contain their content when viewed in ambient mode. CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT); - if (!ambient || TextUtils.isEmpty(text)) { + if (TextUtils.isEmpty(text)) { text = extras.getCharSequence(EXTRA_TEXT); } - this.text = b.processLegacyText(text, ambient); + this.text = b.processLegacyText(text); this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT); return this; } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3adafd725a10..2dc225af6482 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -439,10 +439,11 @@ final class SystemServiceRegistry { }}); registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, - new StaticServiceFetcher<TextServicesManager>() { + new CachedServiceFetcher<TextServicesManager>() { @Override - public TextServicesManager createService() { - return TextServicesManager.getInstance(); + public TextServicesManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return TextServicesManager.createInstance(ctx); }}); registerService(Context.KEYGUARD_SERVICE, KeyguardManager.class, 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/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java new file mode 100644 index 000000000000..6b7f10e8d426 --- /dev/null +++ b/core/java/android/attention/AttentionManagerInternal.java @@ -0,0 +1,73 @@ +/* + * 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.attention; + +/** + * Attention manager local system server interface. + * + * @hide Only for use within the system server. + */ +public abstract class AttentionManagerInternal { + /** + * Returns {@code true} if attention service is supported on this device. + */ + public abstract boolean isAttentionServiceSupported(); + + /** + * Checks whether user attention is at the screen and calls in the provided callback. + * + * @param requestCode a code associated with the attention check request; this code would be + * used to call back in {@link AttentionCallbackInternal#onSuccess} and + * {@link AttentionCallbackInternal#onFailure} + * @param timeoutMillis a budget for the attention check; if it takes longer - {@link + * AttentionCallbackInternal#onFailure} would be called with the {@link + * android.service.attention.AttentionService#ATTENTION_FAILURE_TIMED_OUT} + * code + * @param callback a callback for when the attention check has completed + * @return {@code true} if the attention check should succeed; {@false} otherwise. + */ + public abstract boolean checkAttention(int requestCode, + long timeoutMillis, AttentionCallbackInternal callback); + + /** + * Cancels the specified attention check in case it's no longer needed. + * + * @param requestCode a code provided during {@link #checkAttention} + */ + public abstract void cancelAttentionCheck(int requestCode); + + /** Internal interface for attention callback. */ + public abstract static class AttentionCallbackInternal { + /** + * Provides the result of the attention check, if the check was successful. + * + * @param requestCode a code provided in {@link #checkAttention} + * @param result an int with the result of the check + * @param timestamp a {@code SystemClock.uptimeMillis()} timestamp associated with the + * attention check + */ + public abstract void onSuccess(int requestCode, int result, long timestamp); + + /** + * Provides the explanation for why the attention check had failed. + * + * @param requestCode a code provided in {@link #checkAttention} + * @param error an int with the reason for failure + */ + public abstract void onFailure(int requestCode, int error); + } +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d27cce535c3b..8497656df2be 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -33,6 +33,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; @@ -1954,6 +1955,17 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS"; /** + * Intent extra: ID of the shortcut used to send the share intent. + * + * @see ShortcutInfo#getId() + * + * <p> + * Type: String + * </p> + */ + public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID"; + + /** * Activity action: Launch UI to manage which apps have a given permission. * <p> * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission group diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 740fd7f963f9..b7366f1bbafc 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; @@ -76,9 +77,23 @@ public class CrossProfileApps { } /** - * Starts the specified activity of the caller package in the specified profile if the caller - * has {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} permission and - * both the caller and target user profiles are in the same user group. + * @deprecated use {@link #startActivity(ComponentName, UserHandle)} instead. + * + * @removed + * @hide + */ + @Deprecated + @UnsupportedAppUsage + public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) { + startActivity(component, targetUser); + } + + /** + * Starts the specified activity of the caller package in the specified profile. Unlike + * {@link #startMainActivity}, this can start any activity of the caller package, not just + * the main activity. + * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} + * permission and both the caller and target user profiles must be in the same profile group. * * @param component The ComponentName of the activity to launch. It must be exported. * @param targetUser The UserHandle of the profile, must be one of the users returned by @@ -88,7 +103,7 @@ public class CrossProfileApps { */ @SystemApi @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) - public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) { + public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) { try { mService.startActivityAsUser(mContext.getIApplicationThread(), mContext.getPackageName(), component, targetUser.getIdentifier(), false); diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index 03124be7ab7d..c702b16c97f6 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -16,6 +16,7 @@ package android.content.pm; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; @@ -72,4 +73,7 @@ interface IShortcutService { void applyRestore(in byte[] payload, int user); boolean isRequestPinItemSupported(int user, int requestType); + + // System API used by framework's ShareSheet (ChooserActivity) + ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId); }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 45b5dcac6cc0..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 {} diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java index 8cf66d81972f..f21612a58a53 100644 --- a/core/java/android/content/pm/PermissionGroupInfo.java +++ b/core/java/android/content/pm/PermissionGroupInfo.java @@ -49,7 +49,7 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable { * only access while in the foreground. * * From the "requestDetail" attribute or, if not set, {@link - * android.content.res.ResourceId#ID_NULL}. + * android.content.res.Resources#ID_NULL}. * * @hide */ @@ -61,7 +61,7 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable { * access. Also used when requesting both foreground and background access. * * From the "backgroundRequest" attribute or, if not set, {@link - * android.content.res.ResourceId#ID_NULL}. + * android.content.res.Resources#ID_NULL}. * * @hide */ @@ -73,7 +73,7 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable { * background access. * * From the "backgroundRequestDetail" attribute or, if not set, {@link - * android.content.res.ResourceId#ID_NULL}. + * android.content.res.Resources#ID_NULL}. * * @hide */ 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/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index 2d590033259f..4f7acd96aa6b 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -17,17 +17,22 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.UserIdInt; import android.app.usage.UsageStatsManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.graphics.drawable.AdaptiveIconDrawable; import android.os.Build; import android.os.Build.VERSION_CODES; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; @@ -549,4 +554,85 @@ public class ShortcutManager { protected int injectMyUserId() { return mContext.getUserId(); } + + /** + * Used by framework's ShareSheet (ChooserActivity.java) to retrieve all of the direct share + * targets that match the given IntentFilter. + * + * @param filter IntentFilter that will be used to retrieve the matching {@link ShortcutInfo}s. + * @return List of {@link ShareShortcutInfo}s that match the given IntentFilter. + * @hide + */ + @NonNull + @SystemApi + public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) { + try { + return mService.getShareTargets(mContext.getPackageName(), filter, + injectMyUserId()).getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Represents the result of a query return by {@link #getShareTargets(IntentFilter)}. + * + * @hide + */ + @SystemApi + public static final class ShareShortcutInfo implements Parcelable { + private final ShortcutInfo mShortcutInfo; + private final ComponentName mTargetComponent; + + /** + * @hide + */ + public ShareShortcutInfo(@NonNull ShortcutInfo shortcutInfo, + @NonNull ComponentName targetComponent) { + if (shortcutInfo == null) { + throw new NullPointerException("shortcut info is null"); + } + if (targetComponent == null) { + throw new NullPointerException("target component is null"); + } + + mShortcutInfo = shortcutInfo; + mTargetComponent = targetComponent; + } + + private ShareShortcutInfo(Parcel in) { + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); + mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader()); + } + + public ShortcutInfo getShortcutInfo() { + return mShortcutInfo; + } + + public ComponentName getTargetComponent() { + return mTargetComponent; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mShortcutInfo, flags); + dest.writeParcelable(mTargetComponent, flags); + } + + public static final Parcelable.Creator<ShareShortcutInfo> CREATOR = + new Parcelable.Creator<ShareShortcutInfo>() { + public ShareShortcutInfo createFromParcel(Parcel in) { + return new ShareShortcutInfo(in); + } + + public ShareShortcutInfo[] newArray(int size) { + return new ShareShortcutInfo[size]; + } + }; + } } diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java index c798c99fed90..53b52f535fa0 100644 --- a/core/java/android/content/pm/SuspendDialogInfo.java +++ b/core/java/android/content/pm/SuspendDialogInfo.java @@ -16,7 +16,7 @@ package android.content.pm; -import static android.content.res.ResourceId.ID_NULL; +import static android.content.res.Resources.ID_NULL; import android.annotation.DrawableRes; import android.annotation.NonNull; diff --git a/core/java/android/content/res/ResourceId.java b/core/java/android/content/res/ResourceId.java index adb9cf1c8015..3c7b5fc11164 100644 --- a/core/java/android/content/res/ResourceId.java +++ b/core/java/android/content/res/ResourceId.java @@ -22,12 +22,6 @@ import android.annotation.AnyRes; * @hide */ public final class ResourceId { - - /** - * The {@code null} resource ID. - */ - public static final @AnyRes int ID_NULL = 0; - /** * Checks whether the integer {@code id} is a valid resource ID, as generated by AAPT. * <p>Note that a negative integer is not necessarily an invalid resource ID, and custom diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index c4b315ec90c8..baf64ad72a51 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(); @@ -168,7 +174,7 @@ public class Resources { /** @hide */ public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo, int dark, int deviceDefault) { - if (curTheme != ResourceId.ID_NULL) { + if (curTheme != ID_NULL) { return curTheme; } if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) { diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 77796d9ebdf5..d8564d570a4d 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -855,13 +855,9 @@ public class ResourcesImpl { try { if (file.endsWith(".xml")) { if (file.startsWith("res/color/")) { - ColorStateList csl = loadColorStateList(wrapper, value, id, null); - dr = (csl != null ? new ColorStateListDrawable(csl) : null); + dr = loadColorOrXmlDrawable(wrapper, value, id, density, file); } else { - final XmlResourceParser rp = loadXmlResourceParser( - file, id, value.assetCookie, "drawable"); - dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null); - rp.close(); + dr = loadXmlDrawable(wrapper, value, id, density, file); } } else { final InputStream is = mAssets.openNonAsset( @@ -915,6 +911,33 @@ public class ResourcesImpl { return dr; } + private Drawable loadColorOrXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, + int id, int density, String file) { + try { + ColorStateList csl = loadColorStateList(wrapper, value, id, null); + return new ColorStateListDrawable(csl); + } catch (NotFoundException originalException) { + // If we fail to load as color, try as normal XML drawable + try { + return loadXmlDrawable(wrapper, value, id, density, file); + } catch (Exception ignored) { + // If fallback also fails, throw the original exception + throw originalException; + } + } + } + + private Drawable loadXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, + int id, int density, String file) + throws IOException, XmlPullParserException { + try ( + XmlResourceParser rp = + loadXmlResourceParser(file, id, value.assetCookie, "drawable") + ) { + return Drawable.createFromXmlForDensity(wrapper, rp, density, null); + } + } + /** * Loads a font from XML or resources stream. */ 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/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java index 0cf2d18e5c04..70a9f08ea58a 100644 --- a/core/java/android/hardware/display/ColorDisplayManager.java +++ b/core/java/android/hardware/display/ColorDisplayManager.java @@ -17,6 +17,8 @@ package android.hardware.display; import android.Manifest; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -61,16 +63,30 @@ public final class ColorDisplayManager { * * @param saturationLevel 0-100 (inclusive), where 100 is full saturation * @return whether the saturation level change was applied successfully - * * @hide */ @SystemApi @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) - public boolean setSaturationLevel(int saturationLevel) { + public boolean setSaturationLevel(@IntRange(from = 0, to = 100) int saturationLevel) { return mManager.setSaturationLevel(saturationLevel); } /** + * Set the level of color saturation to apply to a specific app. + * + * @param packageName the package name of the app whose windows should be desaturated + * @param saturationLevel 0-100 (inclusive), where 100 is full saturation + * @return whether the saturation level change was applied successfully + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) + public boolean setAppSaturationLevel(@NonNull String packageName, + @IntRange(from = 0, to = 100) int saturationLevel) { + return mManager.setAppSaturationLevel(packageName, saturationLevel); + } + + /** * Returns {@code true} if Night Display is supported by the device. * * @hide @@ -88,6 +104,16 @@ public final class ColorDisplayManager { return context.getResources().getBoolean(R.bool.config_displayWhiteBalanceAvailable); } + /** + * Check if the color transforms are color accelerated. Some transforms are experimental only + * on non-accelerated platforms due to the performance implications. + * + * @hide + */ + public static boolean isColorTransformAccelerated(Context context) { + return context.getResources().getBoolean(R.bool.config_setColorTransformAccelerated); + } + private static class ColorDisplayManagerInternal { private static ColorDisplayManagerInternal sInstance; @@ -128,5 +154,13 @@ public final class ColorDisplayManager { throw e.rethrowFromSystemServer(); } } + + boolean setAppSaturationLevel(String packageName, int saturationLevel) { + try { + return mCdm.setAppSaturationLevel(packageName, saturationLevel); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } } diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl index 81b82c64461f..644f510d45f9 100644 --- a/core/java/android/hardware/display/IColorDisplayManager.aidl +++ b/core/java/android/hardware/display/IColorDisplayManager.aidl @@ -21,4 +21,5 @@ interface IColorDisplayManager { boolean isDeviceColorManaged(); boolean setSaturationLevel(int saturationLevel); + boolean setAppSaturationLevel(String packageName, int saturationLevel); }
\ No newline at end of file diff --git a/core/java/android/hardware/location/ActivityChangedEvent.java b/core/java/android/hardware/location/ActivityChangedEvent.java deleted file mode 100644 index 16cfe6e23e88..000000000000 --- a/core/java/android/hardware/location/ActivityChangedEvent.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.location; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -import java.security.InvalidParameterException; -import java.util.Arrays; -import java.util.List; - -/** - * A class representing an event for Activity changes. - * - * @hide - */ -public class ActivityChangedEvent implements Parcelable { - private final List<ActivityRecognitionEvent> mActivityRecognitionEvents; - - public ActivityChangedEvent(ActivityRecognitionEvent[] activityRecognitionEvents) { - if (activityRecognitionEvents == null) { - throw new InvalidParameterException( - "Parameter 'activityRecognitionEvents' must not be null."); - } - - mActivityRecognitionEvents = Arrays.asList(activityRecognitionEvents); - } - - @NonNull - public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() { - return mActivityRecognitionEvents; - } - - public static final Creator<ActivityChangedEvent> CREATOR = - new Creator<ActivityChangedEvent>() { - @Override - public ActivityChangedEvent createFromParcel(Parcel source) { - int activityRecognitionEventsLength = source.readInt(); - ActivityRecognitionEvent[] activityRecognitionEvents = - new ActivityRecognitionEvent[activityRecognitionEventsLength]; - source.readTypedArray(activityRecognitionEvents, ActivityRecognitionEvent.CREATOR); - - return new ActivityChangedEvent(activityRecognitionEvents); - } - - @Override - public ActivityChangedEvent[] newArray(int size) { - return new ActivityChangedEvent[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - ActivityRecognitionEvent[] activityRecognitionEventArray = - mActivityRecognitionEvents.toArray(new ActivityRecognitionEvent[0]); - parcel.writeInt(activityRecognitionEventArray.length); - parcel.writeTypedArray(activityRecognitionEventArray, flags); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:"); - - for (ActivityRecognitionEvent event : mActivityRecognitionEvents) { - builder.append("\n "); - builder.append(event.toString()); - } - builder.append("\n]"); - - return builder.toString(); - } -} diff --git a/core/java/android/hardware/location/ActivityRecognitionEvent.java b/core/java/android/hardware/location/ActivityRecognitionEvent.java deleted file mode 100644 index 190030a82d6b..000000000000 --- a/core/java/android/hardware/location/ActivityRecognitionEvent.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.location; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * A class that represents an Activity Recognition Event. - * - * @hide - */ -public class ActivityRecognitionEvent implements Parcelable { - private final String mActivity; - private final int mEventType; - private final long mTimestampNs; - - public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) { - mActivity = activity; - mEventType = eventType; - mTimestampNs = timestampNs; - } - - public String getActivity() { - return mActivity; - } - - public int getEventType() { - return mEventType; - } - - public long getTimestampNs() { - return mTimestampNs; - } - - public static final Creator<ActivityRecognitionEvent> CREATOR = - new Creator<ActivityRecognitionEvent>() { - @Override - public ActivityRecognitionEvent createFromParcel(Parcel source) { - String activity = source.readString(); - int eventType = source.readInt(); - long timestampNs = source.readLong(); - - return new ActivityRecognitionEvent(activity, eventType, timestampNs); - } - - @Override - public ActivityRecognitionEvent[] newArray(int size) { - return new ActivityRecognitionEvent[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(mActivity); - parcel.writeInt(mEventType); - parcel.writeLong(mTimestampNs); - } - - @Override - public String toString() { - return String.format( - "Activity='%s', EventType=%s, TimestampNs=%s", - mActivity, - mEventType, - mTimestampNs); - } -} diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java deleted file mode 100644 index 8acd1ff27917..000000000000 --- a/core/java/android/hardware/location/ActivityRecognitionHardware.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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.location; - -import android.Manifest; -import android.content.Context; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.Log; - -/** - * A class that implements an {@link IActivityRecognitionHardware} backed up by the Activity - * Recognition HAL. - * - * @hide - */ -public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub { - private static final String TAG = "ActivityRecognitionHW"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; - private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" - + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware"; - - private static final int INVALID_ACTIVITY_TYPE = -1; - private static final int NATIVE_SUCCESS_RESULT = 0; - private static final int EVENT_TYPE_DISABLED = 0; - private static final int EVENT_TYPE_ENABLED = 1; - - /** - * Contains the number of supported Event Types. - * - * NOTE: increment this counter every time a new EVENT_TYPE_ is added to - * com.android.location.provider.ActivityRecognitionProvider - */ - private static final int EVENT_TYPE_COUNT = 3; - - private static ActivityRecognitionHardware sSingletonInstance; - private static final Object sSingletonInstanceLock = new Object(); - - private final Context mContext; - private final int mSupportedActivitiesCount; - private final String[] mSupportedActivities; - private final int[][] mSupportedActivitiesEnabledEvents; - private final SinkList mSinks = new SinkList(); - - private static class Event { - public int activity; - public int type; - public long timestamp; - } - - private ActivityRecognitionHardware(Context context) { - nativeInitialize(); - - mContext = context; - mSupportedActivities = fetchSupportedActivities(); - mSupportedActivitiesCount = mSupportedActivities.length; - mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT]; - } - - public static ActivityRecognitionHardware getInstance(Context context) { - synchronized (sSingletonInstanceLock) { - if (sSingletonInstance == null) { - sSingletonInstance = new ActivityRecognitionHardware(context); - } - - return sSingletonInstance; - } - } - - public static boolean isSupported() { - return nativeIsSupported(); - } - - @Override - public String[] getSupportedActivities() { - checkPermissions(); - return mSupportedActivities; - } - - @Override - public boolean isActivitySupported(String activity) { - checkPermissions(); - int activityType = getActivityType(activity); - return activityType != INVALID_ACTIVITY_TYPE; - } - - @Override - public boolean registerSink(IActivityRecognitionHardwareSink sink) { - checkPermissions(); - return mSinks.register(sink); - } - - @Override - public boolean unregisterSink(IActivityRecognitionHardwareSink sink) { - checkPermissions(); - return mSinks.unregister(sink); - } - - @Override - public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) { - checkPermissions(); - - int activityType = getActivityType(activity); - if (activityType == INVALID_ACTIVITY_TYPE) { - return false; - } - - int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs); - if (result == NATIVE_SUCCESS_RESULT) { - mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED; - return true; - } - return false; - } - - @Override - public boolean disableActivityEvent(String activity, int eventType) { - checkPermissions(); - - int activityType = getActivityType(activity); - if (activityType == INVALID_ACTIVITY_TYPE) { - return false; - } - - int result = nativeDisableActivityEvent(activityType, eventType); - if (result == NATIVE_SUCCESS_RESULT) { - mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED; - return true; - } - return false; - } - - @Override - public boolean flush() { - checkPermissions(); - int result = nativeFlush(); - return result == NATIVE_SUCCESS_RESULT; - } - - /** - * Called by the Activity-Recognition HAL. - */ - private void onActivityChanged(Event[] events) { - if (events == null || events.length == 0) { - if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged."); - return; - } - - int eventsLength = events.length; - ActivityRecognitionEvent activityRecognitionEventArray[] = - new ActivityRecognitionEvent[eventsLength]; - for (int i = 0; i < eventsLength; ++i) { - Event event = events[i]; - String activityName = getActivityName(event.activity); - activityRecognitionEventArray[i] = - new ActivityRecognitionEvent(activityName, event.type, event.timestamp); - } - ActivityChangedEvent activityChangedEvent = - new ActivityChangedEvent(activityRecognitionEventArray); - - int size = mSinks.beginBroadcast(); - for (int i = 0; i < size; ++i) { - IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i); - try { - sink.onActivityChanged(activityChangedEvent); - } catch (RemoteException e) { - Log.e(TAG, "Error delivering activity changed event.", e); - } - } - mSinks.finishBroadcast(); - } - - private String getActivityName(int activityType) { - if (activityType < 0 || activityType >= mSupportedActivities.length) { - String message = String.format( - "Invalid ActivityType: %d, SupportedActivities: %d", - activityType, - mSupportedActivities.length); - Log.e(TAG, message); - return null; - } - - return mSupportedActivities[activityType]; - } - - private int getActivityType(String activity) { - if (TextUtils.isEmpty(activity)) { - return INVALID_ACTIVITY_TYPE; - } - - int supportedActivitiesLength = mSupportedActivities.length; - for (int i = 0; i < supportedActivitiesLength; ++i) { - if (activity.equals(mSupportedActivities[i])) { - return i; - } - } - - return INVALID_ACTIVITY_TYPE; - } - - private void checkPermissions() { - mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); - } - - private String[] fetchSupportedActivities() { - String[] supportedActivities = nativeGetSupportedActivities(); - if (supportedActivities != null) { - return supportedActivities; - } - - return new String[0]; - } - - private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> { - @Override - public void onCallbackDied(IActivityRecognitionHardwareSink callback) { - int callbackCount = mSinks.getRegisteredCallbackCount(); - if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount); - if (callbackCount != 0) { - return; - } - // currently there is only one client for this, so if all its sinks have died, we clean - // up after them, this ensures that the AR HAL is not out of sink - for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) { - for (int event = 0; event < EVENT_TYPE_COUNT; ++event) { - disableActivityEventIfEnabled(activity, event); - } - } - } - - private void disableActivityEventIfEnabled(int activityType, int eventType) { - if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) { - return; - } - - int result = nativeDisableActivityEvent(activityType, eventType); - mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED; - String message = String.format( - "DisableActivityEvent: activityType=%d, eventType=%d, result=%d", - activityType, - eventType, - result); - Log.e(TAG, message); - } - } - - // native bindings - static { nativeClassInit(); } - - private static native void nativeClassInit(); - private static native boolean nativeIsSupported(); - - private native void nativeInitialize(); - private native void nativeRelease(); - private native String[] nativeGetSupportedActivities(); - private native int nativeEnableActivityEvent( - int activityType, - int eventType, - long reportLatenceNs); - private native int nativeDisableActivityEvent(int activityType, int eventType); - private native int nativeFlush(); -} diff --git a/core/java/android/hardware/location/IActivityRecognitionHardware.aidl b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl deleted file mode 100644 index bc6b1830cd4f..000000000000 --- a/core/java/android/hardware/location/IActivityRecognitionHardware.aidl +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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/license/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.location; - -import android.hardware.location.IActivityRecognitionHardwareSink; - -/** - * Activity Recognition Hardware provider interface. - * This interface can be used to implement hardware based activity recognition. - * - * @hide - */ -interface IActivityRecognitionHardware { - /** - * Gets an array of supported activities by hardware. - */ - String[] getSupportedActivities(); - - /** - * Returns true if the given activity is supported, false otherwise. - */ - boolean isActivitySupported(in String activityType); - - /** - * Registers a sink with Hardware Activity-Recognition. - */ - boolean registerSink(in IActivityRecognitionHardwareSink sink); - - /** - * Unregisters a sink with Hardware Activity-Recognition. - */ - boolean unregisterSink(in IActivityRecognitionHardwareSink sink); - - /** - * Enables tracking of a given activity/event type, if the activity is supported. - */ - boolean enableActivityEvent(in String activityType, int eventType, long reportLatencyNs); - - /** - * Disables tracking of a given activity/eventy type. - */ - boolean disableActivityEvent(in String activityType, int eventType); - - /** - * Requests hardware for all the activity events detected up to the given point in time. - */ - boolean flush(); -}
\ No newline at end of file diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl deleted file mode 100644 index 3fe645c59a30..000000000000 --- a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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/license/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.location; - -import android.hardware.location.IActivityRecognitionHardware; - -/** - * Activity Recognition Hardware client interface. - * This interface can be used to receive interfaces to implementations of - * {@link IActivityRecognitionHardware}. - * - * @hide - */ -oneway interface IActivityRecognitionHardwareClient { - /** - * Hardware Activity-Recognition availability event. - * - * @param isSupported whether the platform has hardware support for the feature - * @param instance the available instance to provide access to the feature - */ - void onAvailabilityChanged(in boolean isSupported, in IActivityRecognitionHardware instance); -} diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl deleted file mode 100644 index 12e3117259e1..000000000000 --- a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/license/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.location; - -import android.hardware.location.IActivityRecognitionHardware; - -/** - * Activity Recognition Hardware watcher. This interface can be used to receive interfaces to - * implementations of {@link IActivityRecognitionHardware}. - * - * @deprecated use {@link IActivityRecognitionHardwareClient} instead. - - * @hide - */ -interface IActivityRecognitionHardwareWatcher { - /** - * Hardware Activity-Recognition availability event. - */ - void onInstanceChanged(in IActivityRecognitionHardware instance); -} 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/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/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java index be2f9551daff..fcfb72035c19 100644 --- a/core/java/android/net/NetworkSpecifier.java +++ b/core/java/android/net/NetworkSpecifier.java @@ -17,7 +17,7 @@ package android.net; /** - * Describes specific properties of a network for use in a {@link NetworkRequest}. + * Describes specific properties of a requested network for use in a {@link NetworkRequest}. * * Applications cannot instantiate this class by themselves, but can obtain instances of * subclasses of this class via other APIs. @@ -49,4 +49,29 @@ public abstract class NetworkSpecifier { public void assertValidFromUid(int requestorUid) { // empty } + + /** + * Optional method which can be overridden by concrete implementations of NetworkSpecifier to + * perform any redaction of information from the NetworkSpecifier, e.g. if it contains + * sensitive information. The default implementation simply returns the object itself - i.e. + * no information is redacted. A concrete implementation may return a modified (copy) of the + * NetworkSpecifier, or even return a null to fully remove all information. + * <p> + * This method is relevant to NetworkSpecifier objects used by agents - those are shared with + * apps by default. Some agents may store sensitive matching information in the specifier, + * e.g. a Wi-Fi SSID (which should not be shared since it may leak location). Those classes + * can redact to a null. Other agents use the Network Specifier to share public information + * with apps - those should not be redacted. + * <p> + * The default implementation redacts no information. + * + * @return A NetworkSpecifier object to be passed along to the requesting app. + * + * @hide + */ + public NetworkSpecifier redact() { + // TODO (b/122160111): convert default to null once all platform NetworkSpecifiers + // implement this method. + return this; + } } 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/os/AppZygote.java b/core/java/android/os/AppZygote.java index 40cbaf75c02d..950f38167754 100644 --- a/core/java/android/os/AppZygote.java +++ b/core/java/android/os/AppZygote.java @@ -34,8 +34,15 @@ import com.android.internal.annotations.GuardedBy; public class AppZygote { private static final String LOG_TAG = "AppZygote"; + // UID of the Zygote itself private final int mZygoteUid; + // First UID/GID of the range the AppZygote can setuid()/setgid() to + private final int mZygoteUidGidMin; + + // Last UID/GID of the range the AppZygote can setuid()/setgid() to + private final int mZygoteUidGidMax; + private final Object mLock = new Object(); /** @@ -47,9 +54,11 @@ public class AppZygote { private final ApplicationInfo mAppInfo; - public AppZygote(ApplicationInfo appInfo, int zygoteUid) { + public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax) { mAppInfo = appInfo; mZygoteUid = zygoteUid; + mZygoteUidGidMin = uidGidMin; + mZygoteUidGidMax = uidGidMax; } /** @@ -104,7 +113,9 @@ public class AppZygote { "app_zygote", // seInfo abi, // abi abi, // acceptedAbiList - null); // instructionSet + null, // instructionSet + mZygoteUidGidMin, + mZygoteUidGidMax); ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress()); // preload application code in the zygote diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index e84a518c4986..ca3905148ede 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -846,7 +846,16 @@ public class FileUtils { return contains(dir.getAbsolutePath(), file.getAbsolutePath()); } - /** {@hide} */ + /** + * Test if a file lives under the given directory, either as a direct child + * or a distant grandchild. + * <p> + * Both files <em>must</em> have been resolved using + * {@link File#getCanonicalFile()} to avoid symlink or path traversal + * attacks. + * + * @hide + */ public static boolean contains(String dirPath, String filePath) { if (dirPath.equals(filePath)) { return true; diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index e4622dbdc379..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; } 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/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 251c5eebadc4..99a85eaefe53 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -809,6 +809,8 @@ public class ZygoteProcess { * may be different from <code>abi</code> in case the children * spawned from this Zygote only communicate using ABI-safe methods. * @param instructionSet null-ok the instruction set to use. + * @param uidRangeStart The first UID in the range the child zygote may setuid()/setgid() to + * @param uidRangeEnd The last UID in the range the child zygote may setuid()/setgid() to */ public ChildZygoteProcess startChildZygote(final String processClass, final String niceName, @@ -817,13 +819,17 @@ public class ZygoteProcess { String seInfo, String abi, String acceptedAbiList, - String instructionSet) { + String instructionSet, + int uidRangeStart, + int uidRangeEnd) { // Create an unguessable address in the global abstract namespace. final LocalSocketAddress serverAddress = new LocalSocketAddress( processClass + "/" + UUID.randomUUID().toString()); final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(), - Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList}; + Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList, + Zygote.CHILD_ZYGOTE_UID_RANGE_START + uidRangeStart, + Zygote.CHILD_ZYGOTE_UID_RANGE_END + uidRangeEnd}; Process.ProcessStartResult result; try { diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index c79589577366..901570389957 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,31 @@ 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 autofill feature that provides suggestions across all apps when + * the user interacts with input fields. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_AUTOFILL = "autofill"; + + /** + * Namespace for content capture feature used by on-device machine intelligence + * to provide suggestions in a privacy-safe manner. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; + + /** * Namespace for all input-related features that are used at the native level. * These features are applied at reboot. * @@ -78,6 +107,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 +135,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 +156,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 +178,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 757c03e132e6..d5de13c40194 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6026,6 +6026,12 @@ public final class Settings { ANY_STRING_VALIDATOR; /** + * Indicates which clock face to show on lock screen and AOD while docked. + * @hide + */ + private static final String DOCKED_CLOCK_FACE = "docked_clock_face"; + + /** * Set by the system to track if the user needs to see the call to action for * the lockscreen notification policy. * @hide @@ -10952,6 +10958,21 @@ public final class Settings { public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type"; /** + * Whether to try cellular data recovery when a bad network is reported. + * + * @hide + */ + public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK = + "data_stall_recovery_on_bad_network"; + + /** + * Minumim duration in millisecodns between cellular data recovery attempts + * + * @hide + */ + public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS = + "min_duration_between_recovery_steps"; + /** * Whether network service discovery is enabled. * * @hide @@ -14126,7 +14147,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()); } @@ -14149,8 +14170,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, @@ -14172,7 +14192,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/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index 31a5962c7e9a..c43a6668b9c3 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -533,7 +533,10 @@ public class RecoveryController { * service. * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock * screen is required to generate recoverable keys. + * + * @deprecated Use the method {@link #generateKey(String, byte[])} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) public @NonNull Key generateKey(@NonNull String alias) throws InternalRecoveryServiceException, LockScreenRequiredException { @@ -556,6 +559,47 @@ public class RecoveryController { } /** + * Generates a recoverable key with the given {@code alias} and {@code metadata}. + * + * <p>The metadata should contain any data that needs to be cryptographically bound to the + * generated key, but does not need to be encrypted by the key. For example, the metadata can + * be a byte string describing the algorithms and non-secret parameters to be used with the + * key. The supplied metadata can later be obtained via + * {@link WrappedApplicationKey#getMetadata()}. + * + * <p>During the key recovery process, the same metadata has to be supplied via + * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process + * will fail due to the checking of the cryptographic binding. This can help prevent + * potential attacks that try to swap key materials on the backup server and trick the + * application to use keys with different algorithms or parameters. + * + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock + * screen is required to generate recoverable keys. + */ + @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) + public @NonNull Key generateKey(@NonNull String alias, @Nullable byte[] metadata) + throws InternalRecoveryServiceException, LockScreenRequiredException { + try { + String grantAlias = mBinder.generateKeyWithMetadata(alias, metadata); + if (grantAlias == null) { + throw new InternalRecoveryServiceException("null grant alias"); + } + return getKeyFromGrant(grantAlias); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (UnrecoverableKeyException e) { + throw new InternalRecoveryServiceException("Failed to get key from keystore", e); + } catch (ServiceSpecificException e) { + if (e.errorCode == ERROR_INSECURE_USER) { + throw new LockScreenRequiredException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); + } + } + + /** * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code * keyBytes}. * @@ -564,7 +608,9 @@ public class RecoveryController { * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock * screen is required to generate recoverable keys. * + * @deprecated Use the method {@link #importKey(String, byte[], byte[])} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes) throws InternalRecoveryServiceException, LockScreenRequiredException { @@ -587,6 +633,49 @@ public class RecoveryController { } /** + * Imports a recoverable 256-bit AES key with the given {@code alias}, the raw bytes {@code + * keyBytes}, and the {@code metadata}. + * + * <p>The metadata should contain any data that needs to be cryptographically bound to the + * imported key, but does not need to be encrypted by the key. For example, the metadata can + * be a byte string describing the algorithms and non-secret parameters to be used with the + * key. The supplied metadata can later be obtained via + * {@link WrappedApplicationKey#getMetadata()}. + * + * <p>During the key recovery process, the same metadata has to be supplied via + * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process + * will fail due to the checking of the cryptographic binding. This can help prevent + * potential attacks that try to swap key materials on the backup server and trick the + * application to use keys with different algorithms or parameters. + * + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock + * screen is required to generate recoverable keys. + */ + @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) + public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes, + @Nullable byte[] metadata) + throws InternalRecoveryServiceException, LockScreenRequiredException { + try { + String grantAlias = mBinder.importKeyWithMetadata(alias, keyBytes, metadata); + if (grantAlias == null) { + throw new InternalRecoveryServiceException("Null grant alias"); + } + return getKeyFromGrant(grantAlias); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (UnrecoverableKeyException e) { + throw new InternalRecoveryServiceException("Failed to get key from keystore", e); + } catch (ServiceSpecificException e) { + if (e.errorCode == ERROR_INSECURE_USER) { + throw new LockScreenRequiredException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); + } + } + + /** * Gets a key called {@code alias} from the recoverable key store. * * @param alias The key alias. diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java index ae4448f9c908..dbfd655953d6 100644 --- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java +++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java @@ -17,6 +17,7 @@ package android.security.keystore.recovery; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -41,6 +42,8 @@ public final class WrappedApplicationKey implements Parcelable { private String mAlias; // The only supported format is AES-256 symmetric key. private byte[] mEncryptedKeyMaterial; + // The optional metadata that's authenticated (but unencrypted) with the key material. + private byte[] mMetadata; // IMPORTANT! PLEASE READ! // ----------------------- @@ -80,13 +83,23 @@ public final class WrappedApplicationKey implements Parcelable { * @param encryptedKeyMaterial The key material * @return This builder */ - public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) { mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial; return this; } /** + * Sets the metadata that is authenticated (but unecrypted) with the key material. + * + * @param metadata The metadata + * @return This builder + */ + public Builder setMetadata(@Nullable byte[] metadata) { + mInstance.mMetadata = metadata; + return this; + } + + /** * Creates a new {@link WrappedApplicationKey} instance. * * @return new instance @@ -102,9 +115,10 @@ public final class WrappedApplicationKey implements Parcelable { private WrappedApplicationKey() { } /** - * Deprecated - consider using Builder. + * @deprecated Use the builder instead. * @hide */ + @Deprecated public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) { mAlias = Preconditions.checkNotNull(alias); mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial); @@ -124,6 +138,11 @@ public final class WrappedApplicationKey implements Parcelable { return mEncryptedKeyMaterial; } + /** The metadata with the key. */ + public @Nullable byte[] getMetadata() { + return mMetadata; + } + public static final Parcelable.Creator<WrappedApplicationKey> CREATOR = new Parcelable.Creator<WrappedApplicationKey>() { public WrappedApplicationKey createFromParcel(Parcel in) { @@ -139,6 +158,7 @@ public final class WrappedApplicationKey implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(mAlias); out.writeByteArray(mEncryptedKeyMaterial); + out.writeByteArray(mMetadata); } /** @@ -147,6 +167,10 @@ public final class WrappedApplicationKey implements Parcelable { protected WrappedApplicationKey(Parcel in) { mAlias = in.readString(); mEncryptedKeyMaterial = in.createByteArray(); + // Check if there is still data to be read. + if (in.dataAvail() > 0) { + mMetadata = in.createByteArray(); + } } @Override diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java new file mode 100644 index 000000000000..f6e448dc4757 --- /dev/null +++ b/core/java/android/service/attention/AttentionService.java @@ -0,0 +1,160 @@ +/* + * 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.attention; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Abstract base class for Attention service. + * + * <p> An attention service provides attention estimation related features to the system. + * The system's default AttentionService implementation is configured in + * {@code config_AttentionComponent}. If this config has no value, a stub is returned. + * + * See: {@link AttentionManagerService}. + * + * <pre> + * {@literal + * <service android:name=".YourAttentionService" + * android:permission="android.permission.BIND_ATTENTION_SERVICE"> + * </service>} + * </pre> + * + * @hide + */ +@SystemApi +public abstract class AttentionService extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. To be supported, the + * service must also require the {@link android.Manifest.permission#BIND_ATTENTION_SERVICE} + * permission so that other applications can not abuse it. + */ + public static final String SERVICE_INTERFACE = + "android.service.attention.AttentionService"; + + /** Attention is absent. */ + public static final int ATTENTION_SUCCESS_ABSENT = 0; + + /** Attention is present. */ + public static final int ATTENTION_SUCCESS_PRESENT = 1; + + /** Preempted by other camera user. */ + public static final int ATTENTION_FAILURE_PREEMPTED = 2; + + /** Preempted by other camera user. */ + public static final int ATTENTION_FAILURE_TIMED_OUT = 3; + + /** Unknown reasons for failing to determine the attention. */ + public static final int ATTENTION_FAILURE_UNKNOWN = 4; + + /** + * Result codes for when attention check was successful. + * + * @hide + */ + @IntDef(prefix = {"ATTENTION_SUCCESS_"}, value = {ATTENTION_SUCCESS_ABSENT, + ATTENTION_SUCCESS_PRESENT}) + @Retention(RetentionPolicy.SOURCE) + public @interface AttentionSuccessCodes { + } + + /** + * Result codes explaining why attention check was not successful. + * + * @hide + */ + @IntDef(prefix = {"ATTENTION_FAILURE_"}, value = {ATTENTION_FAILURE_PREEMPTED, + ATTENTION_FAILURE_TIMED_OUT, ATTENTION_FAILURE_UNKNOWN}) + @Retention(RetentionPolicy.SOURCE) + public @interface AttentionFailureCodes { + } + + private final IAttentionService.Stub mBinder = new IAttentionService.Stub() { + + /** {@inheritDoc} */ + @Override + public void checkAttention(int requestCode, IAttentionCallback callback) { + Preconditions.checkNotNull(callback); + AttentionService.this.onCheckAttention(requestCode, new AttentionCallback(callback)); + } + + /** {@inheritDoc} */ + @Override + public void cancelAttentionCheck(int requestCode) { + AttentionService.this.onCancelAttentionCheck(requestCode); + } + }; + + @Override + public final IBinder onBind(Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mBinder; + } + return null; + } + + /** + * Checks the user attention and calls into the provided callback. + * + * @param requestCode an identifier that could be used to cancel the request + * @param callback the callback to return the result to + */ + public abstract void onCheckAttention(int requestCode, @NonNull AttentionCallback callback); + + /** Cancels the attention check for a given request code. */ + public abstract void onCancelAttentionCheck(int requestCode); + + + /** Callbacks for AttentionService results. */ + public static final class AttentionCallback { + private final IAttentionCallback mCallback; + + private AttentionCallback(IAttentionCallback callback) { + mCallback = callback; + } + + /** Returns the result. */ + public void onSuccess(int requestCode, @AttentionSuccessCodes int result, long timestamp) { + try { + mCallback.onSuccess(requestCode, result, timestamp); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Signals a failure. */ + public void onFailure(int requestCode, @AttentionFailureCodes int error) { + try { + mCallback.onFailure(requestCode, error); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } +} diff --git a/core/java/android/service/attention/IAttentionCallback.aidl b/core/java/android/service/attention/IAttentionCallback.aidl new file mode 100644 index 000000000000..0e8a1e75c14b --- /dev/null +++ b/core/java/android/service/attention/IAttentionCallback.aidl @@ -0,0 +1,27 @@ + /* + * 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.attention; + +/** + * Callback for onCheckAttention request. + * + * @hide + */ +oneway interface IAttentionCallback { + void onSuccess(int requestCode, int result, long timestamp); + void onFailure(int requestCode, int error); +} diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl b/core/java/android/service/attention/IAttentionService.aidl index 21c8e87e6c6d..c3b6f48a5b2e 100644 --- a/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl +++ b/core/java/android/service/attention/IAttentionService.aidl @@ -1,11 +1,11 @@ /* - * Copyright (C) 2014, The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/license/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,19 +14,16 @@ * limitations under the License. */ -package android.hardware.location; +package android.service.attention; -import android.hardware.location.ActivityChangedEvent; +import android.service.attention.IAttentionCallback; /** - * Activity Recognition Hardware provider Sink interface. - * This interface can be used to implement sinks to receive activity notifications. + * Interface for a concrete implementation to provide to the AttentionManagerService. * * @hide */ -interface IActivityRecognitionHardwareSink { - /** - * Activity changed event. - */ - void onActivityChanged(in ActivityChangedEvent event); +oneway interface IAttentionService { + void checkAttention(int requestCode, IAttentionCallback callback); + void cancelAttentionCheck(int requestCode); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index 4be1f9cd6ae5..4dc10cd2e4cc 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -184,6 +184,12 @@ public abstract class EuiccService extends Service { public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; + /** + * Intent extra set for resolution requests containing an int indicating the current card Id. + */ + public static final String EXTRA_RESOLUTION_CARD_ID = + "android.service.euicc.extra.RESOLUTION_CARD_ID"; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "RESULT_" }, value = { diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java index 5718d994620d..70d049a6e985 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -162,6 +162,7 @@ public final class Half extends Number implements Comparable<Half> { private static final int FP32_EXPONENT_MASK = 0xff; private static final int FP32_SIGNIFICAND_MASK = 0x7fffff; private static final int FP32_EXPONENT_BIAS = 127; + private static final int FP32_QNAN_MASK = 0x400000; private static final int FP32_DENORMAL_MAGIC = 126 << 23; private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); @@ -903,6 +904,9 @@ public final class Half extends Number implements Comparable<Half> { outM = m << 13; if (e == 0x1f) { // Infinite or NaN outE = 0xff; + if (outM != 0) { // SNaNs are quieted + outM |= FP32_QNAN_MASK; + } } else { outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS; } diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 91bc3eb5f83a..e4c8eeb3b9b0 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -43,6 +43,9 @@ public class TimeUtils { /** {@hide} */ private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + /** @hide */ + public static final SimpleDateFormat sDumpDateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); /** * Tries to return a time zone that would have had the specified offset * and DST value at the specified moment in the specified country. @@ -360,4 +363,28 @@ public class TimeUtils { return sLoggingFormat.format(new Date(millis)); } } -} + + /** + * Dump a currentTimeMillis style timestamp for dumpsys. + * + * @hide + */ + public static void dumpTime(PrintWriter pw, long time) { + pw.print(sDumpDateFormat.format(new Date(time))); + } + + /** + * Dump a currentTimeMillis style timestamp for dumpsys, with the delta time from now. + * + * @hide + */ + public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) { + pw.print(sDumpDateFormat.format(new Date(time))); + if (time == now) { + pw.print(" (now)"); + } else { + pw.print(" ("); + TimeUtils.formatDuration(time, now, pw); + pw.print(")"); + } + }} diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 4ead34ed2f97..f58efc900427 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1391,11 +1391,17 @@ public final class Display { */ public static final int HDR_TYPE_HLG = 3; + /** + * HDR10+ display. + */ + public static final int HDR_TYPE_HDR10_PLUS = 4; + /** @hide */ @IntDef(prefix = { "HDR_TYPE_" }, value = { HDR_TYPE_DOLBY_VISION, HDR_TYPE_HDR10, HDR_TYPE_HLG, + HDR_TYPE_HDR10_PLUS, }) @Retention(RetentionPolicy.SOURCE) public @interface HdrType {} diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 9cced4e0052f..f9a46b13d0e1 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -818,7 +818,10 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final int KEYCODE_THUMBS_UP = 286; /** Key code constant: Thumbs down key. Apps can use this to let user downvote content. */ public static final int KEYCODE_THUMBS_DOWN = 287; - /** Key code constant: Consumed by system to switch current viewer profile. */ + /** + * Key code constant: Used to switch current {@link android.accounts.Account} that is + * consuming content. May be consumed by system to set account globally. + */ public static final int KEYCODE_PROFILE_SWITCH = 288; /** 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/PointerIcon.java b/core/java/android/view/PointerIcon.java index cf11fd04efdf..c3d13bd46eb3 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -31,6 +31,7 @@ import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -144,7 +145,8 @@ public final class PointerIcon implements Parcelable { public static final int TYPE_DEFAULT = TYPE_ARROW; private static final PointerIcon gNullIcon = new PointerIcon(TYPE_NULL); - private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>(); + private static final SparseArray<SparseArray<PointerIcon>> gSystemIconsByDisplay = + new SparseArray<SparseArray<PointerIcon>>(); private static boolean sUseLargeIcons = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -163,6 +165,12 @@ public final class PointerIcon implements Parcelable { @UnsupportedAppUsage private int mDurationPerFrame; + /** + * Listener for displays lifecycle. + * @hide + */ + private static DisplayManager.DisplayListener sDisplayListener; + private PointerIcon(int type) { mType = type; } @@ -211,7 +219,19 @@ public final class PointerIcon implements Parcelable { return gNullIcon; } - PointerIcon icon = gSystemIcons.get(type); + if (sDisplayListener == null) { + registerDisplayListener(context); + } + + final int displayId = context.getDisplayId(); + SparseArray<PointerIcon> systemIcons = gSystemIconsByDisplay.get(displayId); + if (systemIcons == null) { + systemIcons = new SparseArray<>(); + gSystemIconsByDisplay.put(displayId, systemIcons); + } + + PointerIcon icon = systemIcons.get(type); + // Reload if not in the same display. if (icon != null) { return icon; } @@ -240,7 +260,7 @@ public final class PointerIcon implements Parcelable { } else { icon.loadResource(context, context.getResources(), resourceId); } - gSystemIcons.append(type, icon); + systemIcons.append(type, icon); return icon; } @@ -250,7 +270,7 @@ public final class PointerIcon implements Parcelable { */ public static void setUseLargeIcons(boolean use) { sUseLargeIcons = use; - gSystemIcons.clear(); + gSystemIconsByDisplay.clear(); } /** @@ -576,4 +596,30 @@ public final class PointerIcon implements Parcelable { return 0; } } + + /** + * Manage system icon cache handled by display lifecycle. + * @param context The context. + */ + private static void registerDisplayListener(@NonNull Context context) { + sDisplayListener = new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + } + + @Override + public void onDisplayRemoved(int displayId) { + gSystemIconsByDisplay.remove(displayId); + } + + @Override + public void onDisplayChanged(int displayId) { + gSystemIconsByDisplay.remove(displayId); + } + }; + + DisplayManager displayManager = context.getSystemService(DisplayManager.class); + displayManager.registerDisplayListener(sDisplayListener, null /* handler */); + } + } 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/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index 04e725e64e9c..63c21f352e74 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 @@ -88,10 +88,10 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override - void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, - int flags) { - getMainCaptureSession().notifyViewTextChanged(mId, id, text, flags); + void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) { + getMainCaptureSession().notifyViewTextChanged(mId, id, text); } + @Override boolean isContentCaptureEnabled() { return getMainCaptureSession().isContentCaptureEnabled(); diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 0d064307ce54..43963c3054e5 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; import android.view.autofill.AutofillId; import com.android.internal.util.Preconditions; @@ -28,11 +29,15 @@ import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** @hide */ @SystemApi public final class ContentCaptureEvent implements Parcelable { + private static final String TAG = ContentCaptureEvent.class.getSimpleName(); + /** @hide */ public static final int TYPE_SESSION_FINISHED = -2; /** @hide */ @@ -46,9 +51,11 @@ public final class ContentCaptureEvent implements Parcelable { public static final int TYPE_VIEW_APPEARED = 1; /** - * Called when a node has been removed from the screen and is not visible to the user anymore. + * Called when one or more nodes have been removed from the screen and is not visible to the + * user anymore. * - * <p>The id of the node is available through {@link #getId()}. + * <p>To get the id(s), first call {@link #getIds()} - if it returns {@code null}, then call + * {@link #getId()}. */ public static final int TYPE_VIEW_DISAPPEARED = 2; @@ -74,29 +81,23 @@ public final class ContentCaptureEvent implements Parcelable { private final @NonNull String mSessionId; private final int mType; private final long mEventTime; - private final int mFlags; private @Nullable AutofillId mId; + private @Nullable ArrayList<AutofillId> mIds; private @Nullable ViewNode mNode; private @Nullable CharSequence mText; private @Nullable String mParentSessionId; private @Nullable ContentCaptureContext mClientContext; /** @hide */ - public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) { + public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime) { mSessionId = sessionId; mType = type; mEventTime = eventTime; - mFlags = flags; - } - - /** @hide */ - public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) { - this(sessionId, type, System.currentTimeMillis(), flags); } /** @hide */ public ContentCaptureEvent(@NonNull String sessionId, int type) { - this(sessionId, type, /* flags= */ 0); + this(sessionId, type, System.currentTimeMillis()); } /** @hide */ @@ -105,6 +106,27 @@ public final class ContentCaptureEvent implements Parcelable { return this; } + private void setAutofillIds(@NonNull ArrayList<AutofillId> ids) { + mIds = Preconditions.checkNotNull(ids); + } + + /** + * Adds an autofill id to the this event, merging the single id into a list if necessary. + * @hide */ + public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) { + if (mIds == null) { + mIds = new ArrayList<>(); + if (mId == null) { + Log.w(TAG, "addAutofillId(" + id + ") called without an initial id"); + } else { + mIds.add(mId); + mId = null; + } + } + mIds.add(id); + return this; + } + /** * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. * @@ -183,16 +205,6 @@ public final class ContentCaptureEvent implements Parcelable { } /** - * Gets optional flags associated with the event. - * - * @return either {@code 0} or - * {@link android.view.contentcapture.ContentCaptureSession#FLAG_USER_INPUT}. - */ - public int getFlags() { - return mFlags; - } - - /** * Gets the whole metadata of the node associated with the event. * * <p>Only set on {@link #TYPE_VIEW_APPEARED} events. @@ -205,7 +217,9 @@ public final class ContentCaptureEvent implements Parcelable { /** * Gets the {@link AutofillId} of the node associated with the event. * - * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED} and {@link #TYPE_VIEW_TEXT_CHANGED} events. + * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED} (when the event contains just one node - if + * it contains more than one, this method returns {@code null} and the actual ids should be + * retrived by {@link #getIds()}) and {@link #TYPE_VIEW_TEXT_CHANGED} events. */ @Nullable public AutofillId getId() { @@ -213,6 +227,17 @@ public final class ContentCaptureEvent implements Parcelable { } /** + * Gets the {@link AutofillId AutofillIds} of the nodes associated with the event. + * + * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED}, when the event contains more than one node + * (if it contains just one node, it's returned by {@link #getId()} instead. + */ + @Nullable + public List<AutofillId> getIds() { + return mIds; + } + + /** * Gets the current text of the node associated with the event. * * <p>Only set on {@link #TYPE_VIEW_TEXT_CHANGED} events. @@ -226,12 +251,12 @@ public final class ContentCaptureEvent implements Parcelable { public void dump(@NonNull PrintWriter pw) { pw.print("type="); pw.print(getTypeAsString(mType)); pw.print(", time="); pw.print(mEventTime); - if (mFlags > 0) { - pw.print(", flags="); pw.print(mFlags); - } if (mId != null) { pw.print(", id="); pw.print(mId); } + if (mIds != null) { + pw.print(", ids="); pw.print(mIds); + } if (mNode != null) { pw.print(", mNode.id="); pw.print(mNode.getAutofillId()); } @@ -255,12 +280,12 @@ public final class ContentCaptureEvent implements Parcelable { if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) { string.append(", parent=").append(mParentSessionId); } - if (mFlags > 0) { - string.append(", flags=").append(mFlags); - } if (mId != null) { string.append(", id=").append(mId); } + if (mIds != null) { + string.append(", ids=").append(mIds); + } if (mNode != null) { final String className = mNode.getClassName(); if (mNode != null) { @@ -281,8 +306,8 @@ public final class ContentCaptureEvent implements Parcelable { parcel.writeString(mSessionId); parcel.writeInt(mType); parcel.writeLong(mEventTime); - parcel.writeInt(mFlags); parcel.writeParcelable(mId, flags); + parcel.writeTypedList(mIds); ViewNode.writeToParcel(parcel, mNode, flags); parcel.writeCharSequence(mText); if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { @@ -301,13 +326,15 @@ public final class ContentCaptureEvent implements Parcelable { final String sessionId = parcel.readString(); final int type = parcel.readInt(); final long eventTime = parcel.readLong(); - final int flags = parcel.readInt(); - final ContentCaptureEvent event = - new ContentCaptureEvent(sessionId, type, eventTime, flags); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime); final AutofillId id = parcel.readParcelable(null); if (id != null) { event.setAutofillId(id); } + final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); + if (ids != null) { + event.setAutofillIds(ids); + } final ViewNode node = ViewNode.readFromParcel(parcel); if (node != null) { event.setViewNode(node); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 81b2e01dfbc7..413f1a5a8955 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -24,10 +24,11 @@ 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; @@ -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. */ @@ -89,24 +88,13 @@ public final class ContentCaptureManager { 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()); } /** @@ -154,8 +142,8 @@ public final class ContentCaptureManager { * * @hide */ - public void flush() { - getMainContentCaptureSession().flush(); + public void flush(@FlushReason int reason) { + getMainContentCaptureSession().flush(reason); } /** diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 7bfc5b413615..b620ab1eb7c3 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -19,6 +19,7 @@ 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; @@ -30,11 +31,14 @@ import android.view.contentcapture.ViewNode.ViewStructureImpl; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; 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; @@ -47,12 +51,6 @@ public abstract class ContentCaptureSession implements AutoCloseable { private static final String TAG = ContentCaptureSession.class.getSimpleName(); /** - * Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the - * text change was caused by user input (for example, through IME). - */ - public static final int FLAG_USER_INPUT = 0x1; - - /** * Initial state, when there is no session. * * @hide @@ -125,6 +123,32 @@ public abstract class ContentCaptureSession implements AutoCloseable { 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(); @@ -212,7 +236,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. @@ -250,7 +274,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { } try { - flush(); + flush(FLUSH_REASON_SESSION_FINISHED); } finally { onDestroy(); } @@ -316,12 +340,36 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract void internalNotifyViewDisappeared(@NonNull AutofillId id); /** + * Notifies the Content Capture Service that many nodes has been removed from a virtual view + * structure. + * + * <p>Should only be called by views that handle their own virtual view hierarchy. + * + * @param hostId id of the view hosting the virtual hierarchy. + * @param virtualIds ids of the virtual children. + * + * @throws IllegalArgumentException if the {@code hostId} is an autofill id for a virtual view. + * @throws IllegalArgumentException if {@code virtualIds} is empty + */ + public final void notifyViewsDisappeared(@NonNull AutofillId hostId, + @NonNull int[] virtualIds) { + Preconditions.checkArgument(!hostId.isVirtual(), "parent cannot be virtual"); + Preconditions.checkArgument(!ArrayUtils.isEmpty(virtualIds), "virtual ids cannot be empty"); + if (!isContentCaptureEnabled()) return; + + // TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is + // parcelized + for (int id : virtualIds) { + internalNotifyViewDisappeared(new AutofillId(hostId, id, getIdAsInt())); + } + } + + /** * Notifies the Intelligence Service that the value of a text node has been changed. * * @param id of the node. * @param text new text. - * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly - * changed by the user (for example, through the keyboard). + * @param flags currently ignored. */ public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, int flags) { @@ -329,11 +377,11 @@ public abstract class ContentCaptureSession implements AutoCloseable { if (!isContentCaptureEnabled()) return; - internalNotifyViewTextChanged(id, text, flags); + internalNotifyViewTextChanged(id, text); } - abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, - int flags); + abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, + @Nullable CharSequence text); /** * Creates a {@link ViewStructure} for a "standard" view. @@ -407,12 +455,31 @@ public abstract class ContentCaptureSession implements AutoCloseable { return mId; } - /** - * @hide - */ + /** @hide */ @NonNull protected static String getStateAsString(int state) { 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 "UNKOWN-" + reason; + } + } } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index a3ff8c09aa0c..103d7e6dc256 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; @@ -131,6 +131,9 @@ 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, @@ -172,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 @@ -264,24 +268,28 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) { - if (!handleHasStarted() - && event.getType() != ContentCaptureEvent.TYPE_SESSION_STARTED) { + 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(event.getType()) + + ContentCaptureEvent.getTypeAsString(eventType) + "): session not started yet"); return; } + if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event); if (mEvents == null) { if (VERBOSE) { Log.v(TAG, "handleSendEvent(" + getDebugState() + ", " - + ContentCaptureEvent.getTypeAsString(event.getType()) - + "): cCreating buffer for " + MAX_BUFFER_SIZE + " events"); + + 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) { + // Some type of events can be merged together + boolean addEvent = true; + + if (!mEvents.isEmpty() && eventType == TYPE_VIEW_TEXT_CHANGED) { final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1); // TODO(b/121045053): check if flags match @@ -292,10 +300,24 @@ public final class MainContentCaptureSession extends ContentCaptureSession { + event.getText()); } lastEvent.setText(event.getText()); - } else { - mEvents.add(event); + addEvent = false; } - } else { + } + + if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) { + final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1); + if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED + && event.getSessionId().equals(lastEvent.getSessionId())) { + if (VERBOSE) { + Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session " + + lastEvent.getSessionId()); + } + lastEvent.addAutofillId(event.getId()); + addEvent = false; + } + } + + if (addEvent) { mEvents.add(event); } @@ -304,7 +326,7 @@ 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; } @@ -323,15 +345,26 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // 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 boolean handleHasStarted() { return mState != UNKNWON_STATE; } - private void handleScheduleFlush(boolean checkExisting) { + private void handleScheduleFlush(@FlushReason int reason, boolean checkExisting) { if (!handleHasStarted()) { Log.v(TAG, "handleScheduleFlush(" + getDebugState() + "): session not started yet"); return; @@ -340,43 +373,51 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // "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, "handleScheduleFlush(" + getDebugState() + "): scheduled to flush in " - + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.formatUptime(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(" + 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 " + getDebugState()); - } + mFlushHistory.log(logRecord); mHandler.removeMessages(MSG_FLUSH); final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents(); @@ -449,9 +490,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override - void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, - int flags) { - notifyViewTextChanged(mId, id, text, flags); + void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) { + notifyViewTextChanged(mId, id, text); } @Override @@ -492,15 +532,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id, - @Nullable CharSequence text, int flags) { + @Nullable CharSequence text) { mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id) + new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id) .setText(text), /* forceFlush= */ false)); } @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) { @@ -535,8 +574,12 @@ 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); } diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 28d9fcf57370..f99afe6cdab9 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -16,11 +16,15 @@ package android.view.inputmethod; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.InputType; import android.text.TextUtils; import android.util.Printer; @@ -472,6 +476,26 @@ public class EditorInfo implements InputType, Parcelable { public String[] contentMimeTypes = null; /** + * If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no + * matter what user ID the calling process has. + * + * <p>Note: This field is silently ignored when:</p> + * <ul> + * <li>{@link android.view.inputmethod.InputMethodSystemProperty#PER_PROFILE_IME_ENABLED} is + * {@code false}.</li> + * <li>{@link android.view.inputmethod.InputMethodSystemProperty#MULTI_CLIENT_IME_ENABLED} + * is {@code true}.</li> + * </ul> + * + * <p>Note also that pseudo handles such as {@link UserHandle#ALL} are not supported.</p> + * + * @hide + */ + @RequiresPermission(INTERACT_ACROSS_USERS_FULL) + @Nullable + public UserHandle targetInputMethodUser = null; + + /** * Ensure that the data in this EditorInfo is compatible with an application * that was developed against the given target API version. This can * impact the following input types: @@ -527,6 +551,9 @@ public class EditorInfo implements InputType, Parcelable { pw.println(prefix + "extras=" + extras); pw.println(prefix + "hintLocales=" + hintLocales); pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes)); + if (targetInputMethodUser != null) { + pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier()); + } } /** @@ -556,6 +583,7 @@ public class EditorInfo implements InputType, Parcelable { LocaleList.getEmptyLocaleList().writeToParcel(dest, flags); } dest.writeStringArray(contentMimeTypes); + UserHandle.writeToParcel(targetInputMethodUser, dest); } /** @@ -582,6 +610,7 @@ public class EditorInfo implements InputType, Parcelable { LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source); res.hintLocales = hintLocales.isEmpty() ? null : hintLocales; res.contentMimeTypes = source.readStringArray(); + res.targetInputMethodUser = UserHandle.readFromParcel(source); return res; } diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java index ea94ad4c5df8..7b9a507ee45d 100644 --- a/core/java/android/view/inspector/InspectableNodeName.java +++ b/core/java/android/view/inspector/InspectableNodeName.java @@ -19,6 +19,8 @@ package android.view.inspector; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.annotation.TestApi; + import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -39,6 +41,7 @@ import java.lang.annotation.Target; */ @Target({TYPE}) @Retention(SOURCE) +@TestApi public @interface InspectableNodeName { /** * The display name for nodes of this type. diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java index e2a763e22288..355ff1d85e1f 100644 --- a/core/java/android/view/inspector/InspectableProperty.java +++ b/core/java/android/view/inspector/InspectableProperty.java @@ -20,7 +20,8 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.content.res.ResourceId; +import android.annotation.TestApi; +import android.content.res.Resources; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -40,6 +41,7 @@ import java.lang.annotation.Target; */ @Target({METHOD}) @Retention(SOURCE) +@TestApi public @interface InspectableProperty { /** * The name of the property. @@ -57,16 +59,16 @@ public @interface InspectableProperty { * If left as {ID_NULL}, and {@link #hasAttributeId()} is true, the attribute ID will be * inferred from {@link #name()}. * - * @return The attribute ID of the property or {@link ResourceId#ID_NULL} + * @return The attribute ID of the property or {@link Resources#ID_NULL} */ - int attributeId() default ResourceId.ID_NULL; + int attributeId() default Resources.ID_NULL; /** * If this property has an attribute ID. * * Set to false if the annotated property does not have an attribute ID, that is, it is not * inflated from an XML attribute. This will prevent the automatic inference of the attribute - * ID if {@link #attributeId()} is set to {@link ResourceId#ID_NULL}. + * ID if {@link #attributeId()} is set to {@link Resources#ID_NULL}. * * @return Whether to infer an attribute ID if not supplied */ @@ -86,7 +88,6 @@ public @interface InspectableProperty { * * @return An array of {@link EnumMap}, empty if not applicable * @see android.annotation.IntDef - * @see IntEnumMapping */ EnumMap[] enumMapping() default {}; @@ -109,6 +110,7 @@ public @interface InspectableProperty { */ @Target({TYPE}) @Retention(SOURCE) + @TestApi @interface EnumMap { /** * The string name of this enumeration value. @@ -133,6 +135,7 @@ public @interface InspectableProperty { */ @Target({TYPE}) @Retention(SOURCE) + @TestApi @interface FlagMap { /** * The string name of this flag. @@ -167,15 +170,22 @@ public @interface InspectableProperty { * * @hide */ + @TestApi enum ValueType { /** * No special handling, property is considered to be a numeric value. + * + * @hide */ + @TestApi NONE, /** * The default the annotation processor infers the value type from context. + * + * @hide */ + @TestApi INFERRED, /** @@ -184,7 +194,9 @@ public @interface InspectableProperty { * This is inferred if {@link #enumMapping()} is specified. * * @see EnumMap + * @hide */ + @TestApi INT_ENUM, /** @@ -193,7 +205,9 @@ public @interface InspectableProperty { * This is inferred if {@link #flagMapping()} is specified. * * @see FlagMap + * @hide */ + @TestApi INT_FLAG, /** @@ -203,7 +217,9 @@ public @interface InspectableProperty { * {@link android.annotation.ColorLong} on the getter method. * * @see android.graphics.Color + * @hide */ + @TestApi COLOR, /** @@ -212,7 +228,9 @@ public @interface InspectableProperty { * This type is not inferred, and is non-trivial to represent using {@link FlagMap}. * * @see android.view.Gravity + * @hide */ + @TestApi GRAVITY } } diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 9733701ee0f0..f553ca512881 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -27,7 +27,6 @@ import android.util.Log; import com.android.internal.textservice.ISpellCheckerSession; import com.android.internal.textservice.ISpellCheckerSessionListener; -import com.android.internal.textservice.ITextServicesManager; import com.android.internal.textservice.ITextServicesSessionListener; import dalvik.system.CloseGuard; @@ -96,7 +95,7 @@ public class SpellCheckerSession { private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2; private final InternalListener mInternalListener; - private final ITextServicesManager mTextServicesManager; + private final TextServicesManager mTextServicesManager; private final SpellCheckerInfo mSpellCheckerInfo; @UnsupportedAppUsage private final SpellCheckerSessionListener mSpellCheckerSessionListener; @@ -124,7 +123,7 @@ public class SpellCheckerSession { * @hide */ public SpellCheckerSession( - SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) { + SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener) { if (info == null || listener == null || tsm == null) { throw new NullPointerException(); } @@ -166,12 +165,8 @@ public class SpellCheckerSession { */ public void close() { mGuard.close(); - try { - mSpellCheckerSessionListenerImpl.close(); - mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl); - } catch (RemoteException e) { - // do nothing - } + mSpellCheckerSessionListenerImpl.close(); + mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl); } /** diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index 5dc8b19444b0..9ff64d9b268a 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -16,16 +16,20 @@ package android.view.textservice; +import android.annotation.NonNull; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; +import android.annotation.UserIdInt; import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.os.UserHandle; import android.util.Log; import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; +import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import java.util.Locale; @@ -67,17 +71,41 @@ public final class TextServicesManager { private static final String TAG = TextServicesManager.class.getSimpleName(); private static final boolean DBG = false; + /** + * @deprecated Do not use. Just kept because of {@link UnsupportedAppUsage} in + * {@link #getInstance()}. + */ + @Deprecated private static TextServicesManager sInstance; private final ITextServicesManager mService; - private TextServicesManager() throws ServiceNotFoundException { + @UserIdInt + private final int mUserId; + + private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException { mService = ITextServicesManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE)); + mUserId = userId; + } + + /** + * The factory method of {@link TextServicesManager}. + * + * @param context {@link Context} from which {@link TextServicesManager} should be instantiated. + * @return {@link TextServicesManager} that is associated with {@link Context#getUserId()}. + * @throws ServiceNotFoundException When {@link TextServicesManager} is not available. + * @hide + */ + @NonNull + public static TextServicesManager createInstance(@NonNull Context context) + throws ServiceNotFoundException { + return new TextServicesManager(context.getUserId()); } /** - * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist. + * @deprecated Do not use. Just kept because of {@link UnsupportedAppUsage} in + * {@link #getInstance()}. * @hide */ @UnsupportedAppUsage @@ -85,7 +113,7 @@ public final class TextServicesManager { synchronized (TextServicesManager.class) { if (sInstance == null) { try { - sInstance = new TextServicesManager(); + sInstance = new TextServicesManager(UserHandle.myUserId()); } catch (ServiceNotFoundException e) { throw new IllegalStateException(e); } @@ -136,7 +164,7 @@ public final class TextServicesManager { final SpellCheckerInfo sci; try { - sci = mService.getCurrentSpellChecker(null); + sci = mService.getCurrentSpellChecker(mUserId, null); } catch (RemoteException e) { return null; } @@ -174,9 +202,9 @@ public final class TextServicesManager { if (subtypeInUse == null) { return null; } - final SpellCheckerSession session = new SpellCheckerSession(sci, mService, listener); + final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener); try { - mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(), + mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(), session.getTextServicesSessionListener(), session.getSpellCheckerSessionListener(), bundle); } catch (RemoteException e) { @@ -191,7 +219,7 @@ public final class TextServicesManager { @UnsupportedAppUsage public SpellCheckerInfo[] getEnabledSpellCheckers() { try { - final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers(); + final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers(mUserId); if (DBG) { Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null")); } @@ -208,7 +236,7 @@ public final class TextServicesManager { public SpellCheckerInfo getCurrentSpellChecker() { try { // Passing null as a locale for ICS - return mService.getCurrentSpellChecker(null); + return mService.getCurrentSpellChecker(mUserId, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -221,7 +249,7 @@ public final class TextServicesManager { public SpellCheckerSubtype getCurrentSpellCheckerSubtype( boolean allowImplicitlySelectedSubtype) { try { - return mService.getCurrentSpellCheckerSubtype(allowImplicitlySelectedSubtype); + return mService.getCurrentSpellCheckerSubtype(mUserId, allowImplicitlySelectedSubtype); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -233,7 +261,15 @@ public final class TextServicesManager { @UnsupportedAppUsage public boolean isSpellCheckerEnabled() { try { - return mService.isSpellCheckerEnabled(); + return mService.isSpellCheckerEnabled(mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void finishSpellCheckerService(ISpellCheckerSessionListener listener) { + try { + mService.finishSpellCheckerService(mUserId, listener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } 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/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index 9f7aa6a2852a..29b3b3cff044 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -160,7 +160,9 @@ public class WebViewZygote { "webview_zygote", // seInfo sPackage.applicationInfo.primaryCpuAbi, // abi TextUtils.join(",", Build.SUPPORTED_ABIS), - null); // instructionSet + null, // instructionSet + Process.FIRST_ISOLATED_UID, + Process.LAST_ISOLATED_UID); // All the work below is usually done by LoadedApk, but the zygote can't talk to // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0d16998d17ee..4a6095464b42 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -48,7 +48,6 @@ import android.content.UndoManager; import android.content.res.ColorStateList; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; -import android.content.res.ResourceId; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -889,7 +888,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // sanitize autofill requests. private boolean mTextSetFromXmlOrResourceId = false; // Resource id used to set the text. - private @StringRes int mTextId = ResourceId.ID_NULL; + private @StringRes int mTextId = Resources.ID_NULL; // // End of autofill-related attributes @@ -1180,7 +1179,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_text: textIsSetFromXml = true; - mTextId = a.getResourceId(attr, ResourceId.ID_NULL); + mTextId = a.getResourceId(attr, Resources.ID_NULL); text = a.getText(attr); break; @@ -11031,7 +11030,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId); } - if (mTextId != ResourceId.ID_NULL) { + if (mTextId != Resources.ID_NULL) { try { structure.setTextIdEntry(getResources().getResourceEntryName(mTextId)); } catch (Resources.NotFoundException e) { diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 4523d3b2739f..b4d8322c7552 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -21,19 +21,24 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.LabeledIntent; +import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -75,6 +80,7 @@ import com.google.android.collect.Lists; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -92,6 +98,15 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; + /** + * If set to true, use ShortcutManager to retrieve the matching direct share targets, instead of + * binding to every ChooserTargetService implementation. + */ + // TODO(b/121287573): Replace with a system flag (setprop?) + private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = false; + // TODO(b/121287224): Re-evaluate this limit + private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20; + private static final int QUERY_TARGET_SERVICE_LIMIT = 5; private static final int WATCHDOG_TIMEOUT_MILLIS = 2000; @@ -120,6 +135,7 @@ public class ChooserActivity extends ResolverActivity { private static final int CHOOSER_TARGET_SERVICE_RESULT = 1; private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2; + private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 3; private final Handler mChooserHandler = new Handler() { @Override @@ -158,6 +174,18 @@ public class ChooserActivity extends ResolverActivity { mChooserListAdapter.setShowServiceTargets(true); break; + case SHORTCUT_MANAGER_SHARE_TARGET_RESULT: + if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT"); + if (isDestroyed()) break; + final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj; + if (resultInfo.resultTargets != null) { + mChooserListAdapter.addServiceResults(resultInfo.originalTarget, + resultInfo.resultTargets); + } + sendVoiceChoicesIfNeeded(); + mChooserListAdapter.setShowServiceTargets(true); + break; + default: super.handleMessage(msg); } @@ -552,6 +580,94 @@ public class ChooserActivity extends ResolverActivity { } } + private IntentFilter getTargetIntentFilter() { + try { + final Intent intent = getTargetIntent(); + String dataString = intent.getDataString(); + if (TextUtils.isEmpty(dataString)) { + dataString = intent.getType(); + } + return new IntentFilter(intent.getAction(), dataString); + } catch (Exception e) { + Log.e(TAG, "failed to get target intent filter " + e); + return null; + } + } + + private void queryDirectShareTargets(ChooserListAdapter adapter) { + final IntentFilter filter = getTargetIntentFilter(); + if (filter == null) { + return; + } + + // Need to keep the original DisplayResolveInfos to be able to reconstruct ServiceResultInfo + // and use the old code path. This Ugliness should go away when Sharesheet is refactored. + final List<DisplayResolveInfo> driList = new ArrayList<>(); + int targetsToQuery = 0; + for (int i = 0, n = adapter.getDisplayResolveInfoCount(); i < n; i++) { + final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i); + if (adapter.getScore(dri) == 0) { + // A score of 0 means the app hasn't been used in some time; + // don't query it as it's not likely to be relevant. + continue; + } + driList.add(dri); + targetsToQuery++; + // TODO(b/121287224): Do we need this here? (similar to queryTargetServices) + if (targetsToQuery >= SHARE_TARGET_QUERY_PACKAGE_LIMIT) { + if (DEBUG) { + Log.d(TAG, "queryTargets hit query target limit " + + SHARE_TARGET_QUERY_PACKAGE_LIMIT); + } + break; + } + } + + AsyncTask.execute(() -> { + ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE); + List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter); + + // Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path + // for direct share targets. After ShareSheet is refactored we should use the + // ShareShortcutInfos directly. + for (int i = 0; i < driList.size(); i++) { + List<ChooserTarget> chooserTargets = new ArrayList<>(); + for (int j = 0; j < resultList.size(); j++) { + if (driList.get(i).getResolvedComponentName().equals( + resultList.get(j).getTargetComponent())) { + chooserTargets.add(convertToChooserTarget(resultList.get(j))); + } + } + if (chooserTargets.isEmpty()) { + continue; + } + + final Message msg = Message.obtain(); + msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT; + msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null); + mChooserHandler.sendMessage(msg); + } + }); + } + + private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) { + ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); + Bundle extras = new Bundle(); + extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()); + return new ChooserTarget( + // The name of this target. + shortcutInfo.getShortLabel(), + // Don't load the icon until it is selected to be shown + null, + // The ranking score for this target (0.0-1.0); the system will omit items with low + // scores when there are too many Direct Share items. + 0.5f, + // The name of the component to be launched if this target is chosen. + shareShortcut.getTargetComponent().clone(), + // The extra values here will be merged into the Intent when this target is chosen. + extras); + } + private String convertServiceName(String packageName, String serviceName) { if (TextUtils.isEmpty(serviceName)) { return null; @@ -765,9 +881,8 @@ public class ChooserActivity extends ResolverActivity { } } } - final Icon icon = chooserTarget.getIcon(); - // TODO do this in the background - mDisplayIcon = icon != null ? icon.loadDrawable(ChooserActivity.this) : null; + // TODO(b/121287224): do this in the background thread, and only for selected targets + mDisplayIcon = getChooserTargetIconDrawable(chooserTarget); if (sourceInfo != null) { mBackupResolveInfo = null; @@ -791,6 +906,39 @@ public class ChooserActivity extends ResolverActivity { mModifiedScore = other.mModifiedScore; } + /** + * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip + * the call to LauncherApps#getShortcuts(ShortcutQuery). + */ + // TODO(121287224): Refactor code to apply the suggestion above + private Drawable getChooserTargetIconDrawable(ChooserTarget target) { + final Icon icon = target.getIcon(); + if (icon != null) { + return icon.loadDrawable(ChooserActivity.this); + } + if (!USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) { + return null; + } + + Bundle extras = target.getIntentExtras(); + if (extras == null || !extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) { + return null; + } + CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID); + LauncherApps launcherApps = (LauncherApps) getSystemService( + Context.LAUNCHER_APPS_SERVICE); + final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery(); + q.setPackage(target.getComponentName().getPackageName()); + q.setShortcutIds(Arrays.asList(shortcutId.toString())); + q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC); + final List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(q, getUser()); + if (shortcuts != null && shortcuts.size() > 0) { + return launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0); + } + + return null; + } + public float getModifiedScore() { return mModifiedScore; } @@ -1030,8 +1178,15 @@ public class ChooserActivity extends ResolverActivity { mTargetsNeedPruning = true; } } - if (DEBUG) Log.d(TAG, "List built querying services"); - queryTargetServices(this); + if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) { + if (DEBUG) { + Log.d(TAG, "querying direct share targets from ShortcutManager"); + } + queryDirectShareTargets(this); + } else { + if (DEBUG) Log.d(TAG, "List built querying services"); + queryTargetServices(this); + } } @Override diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 498de53b65e9..70935d4ac076 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -16,7 +16,7 @@ package com.android.internal.app; -import static android.content.res.ResourceId.ID_NULL; +import static android.content.res.Resources.ID_NULL; import android.Manifest; import android.app.AlertDialog; 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/ChildZygoteInit.java b/core/java/com/android/internal/os/ChildZygoteInit.java index f90cd0224596..a052a3b3ab6a 100644 --- a/core/java/com/android/internal/os/ChildZygoteInit.java +++ b/core/java/com/android/internal/os/ChildZygoteInit.java @@ -15,6 +15,7 @@ */ package com.android.internal.os; +import android.os.Process; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -49,6 +50,22 @@ public class ChildZygoteInit { return null; } + static int parseIntFromArg(String[] argv, String desiredArg) { + int value = -1; + for (String arg : argv) { + if (arg.startsWith(desiredArg)) { + String valueStr = arg.substring(arg.indexOf('=') + 1); + try { + value = Integer.parseInt(valueStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid int argument: " + + valueStr, e); + } + } + } + return value; + } + /** * Starts a ZygoteServer and listens for requests * @@ -72,6 +89,27 @@ public class ChildZygoteInit { throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex); } + int uidGidMin = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_START); + int uidGidMax = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_END); + if (uidGidMin == -1 || uidGidMax == -1) { + throw new RuntimeException("Couldn't parse UID range start/end"); + } + if (uidGidMin > uidGidMax) { + throw new RuntimeException("Passed in UID range is invalid, min > max."); + } + + // Verify the UIDs are in the isolated UID range, as that's the only thing that we should + // be forking right now + if (!Process.isIsolated(uidGidMin) || !Process.isIsolated(uidGidMax)) { + throw new RuntimeException("Passed in UID range does not map to isolated processes."); + } + + /** + * Install a seccomp filter that ensure this Zygote can only setuid()/setgid() + * to the passed in range. + */ + Zygote.nativeInstallSeccompUidGidFilter(uidGidMin, uidGidMax); + final Runnable caller; try { server.registerServerSocketAtAbstractName(socketName); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index d720c689f5de..f5746ca65f5e 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -104,6 +104,20 @@ public final class Zygote { */ public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list="; + /** + * An extraArg passed when a zygote process is forking a child-zygote, specifying the + * start of the UID range the children of the Zygote may setuid()/setgid() to. This + * will be enforced with a seccomp filter. + */ + public static final String CHILD_ZYGOTE_UID_RANGE_START = "--uid-range-start="; + + /** + * An extraArg passed when a zygote process is forking a child-zygote, specifying the + * end of the UID range the children of the Zygote may setuid()/setgid() to. This + * will be enforced with a seccomp filter. + */ + public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end="; + private Zygote() {} /** Called for some security initialization before any fork. */ @@ -222,6 +236,13 @@ public final class Zygote { native protected static void nativeAllowFileAcrossFork(String path); /** + * Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range + * @param uidGidMin The smallest allowed uid/gid + * @param uidGidMax The largest allowed uid/gid + */ + native protected static void nativeInstallSeccompUidGidFilter(int uidGidMin, int uidGidMax); + + /** * Zygote unmount storage space on initializing. * This method is called once. */ diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl index 20f2aa06cf1e..8022949d1728 100644 --- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl +++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl @@ -29,12 +29,13 @@ import android.view.textservice.SpellCheckerSubtype; * @hide */ interface ITextServicesManager { - SpellCheckerInfo getCurrentSpellChecker(String locale); - SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean allowImplicitlySelectedSubtype); - oneway void getSpellCheckerService(String sciId, in String locale, + SpellCheckerInfo getCurrentSpellChecker(int userId, String locale); + SpellCheckerSubtype getCurrentSpellCheckerSubtype(int userId, + boolean allowImplicitlySelectedSubtype); + oneway void getSpellCheckerService(int userId, String sciId, in String locale, in ITextServicesSessionListener tsListener, in ISpellCheckerSessionListener scListener, in Bundle bundle); - oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener); - boolean isSpellCheckerEnabled(); - SpellCheckerInfo[] getEnabledSpellCheckers(); + oneway void finishSpellCheckerService(int userId, in ISpellCheckerSessionListener listener); + boolean isSpellCheckerEnabled(int userId); + SpellCheckerInfo[] getEnabledSpellCheckers(int userId); } diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 901cfe3602b1..9fe49b4b045d 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -127,8 +127,10 @@ public final class InputBindResult implements Parcelable { */ int ERROR_IME_NOT_CONNECTED = 8; /** - * Indicates that the caller is not the foreground user (or does not have - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission). + * Indicates that the caller is not the foreground user, does not have + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user + * specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not + * running. */ int ERROR_INVALID_USER = 9; /** diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 591f15fd5676..9a77802aa541 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -62,7 +62,9 @@ interface ILockSettings { in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile); KeyChainSnapshot getKeyChainSnapshot(); String generateKey(String alias); + String generateKeyWithMetadata(String alias, in byte[] metadata); String importKey(String alias, in byte[] keyBytes); + String importKeyWithMetadata(String alias, in byte[] keyBytes, in byte[] metadata); String getKey(String alias); void removeKey(String alias); void setSnapshotCreatedPendingIntent(in PendingIntent intent); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 088e13ff92bc..5be70ef46d31 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -185,7 +185,6 @@ cc_library_shared { "android_hardware_UsbDevice.cpp", "android_hardware_UsbDeviceConnection.cpp", "android_hardware_UsbRequest.cpp", - "android_hardware_location_ActivityRecognitionHardware.cpp", "android_util_FileObserver.cpp", "android/opengl/poly_clip.cpp", // TODO: .arm "android/opengl/util.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 109222298fe5..a586dc1e6d7e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -101,7 +101,6 @@ extern int register_android_hardware_SoundTrigger(JNIEnv *env); extern int register_android_hardware_UsbDevice(JNIEnv *env); extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env); extern int register_android_hardware_UsbRequest(JNIEnv *env); -extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env); extern int register_android_media_AudioEffectDescriptor(JNIEnv *env); extern int register_android_media_AudioRecord(JNIEnv *env); @@ -1457,7 +1456,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_hardware_UsbDevice), REG_JNI(register_android_hardware_UsbDeviceConnection), REG_JNI(register_android_hardware_UsbRequest), - REG_JNI(register_android_hardware_location_ActivityRecognitionHardware), REG_JNI(register_android_media_AudioEffectDescriptor), REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioRecord), diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index bc49771c380b..e2e3042ee5b9 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -557,8 +557,9 @@ namespace PaintGlue { return result; } - // FIXME: Should this be FastNative? - static void setColorLong(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace, + // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow + // passing the SkColorSpace directly from JNI. + static void setColor(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)) { @@ -569,9 +570,11 @@ namespace PaintGlue { 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) { + // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow + // passing the SkColorSpace directly from JNI. + static void setShadowLayer(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; @@ -784,22 +787,6 @@ namespace PaintGlue { obj->setStyle(style); } - static jint getColor(jlong paintHandle) { - int color; - color = reinterpret_cast<Paint*>(paintHandle)->getColor(); - return static_cast<jint>(color); - } - - static jint getAlpha(jlong paintHandle) { - int alpha; - alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha(); - return static_cast<jint>(alpha); - } - - static void setColor(jlong paintHandle, jint color) { - reinterpret_cast<Paint*>(paintHandle)->setColor(color); - } - static void setAlpha(jlong paintHandle, jint a) { reinterpret_cast<Paint*>(paintHandle)->setAlpha(a); } @@ -1047,18 +1034,6 @@ namespace PaintGlue { return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize); } - static void setShadowLayer(jlong paintHandle, jfloat radius, - jfloat dx, jfloat dy, jint color) { - Paint* paint = reinterpret_cast<Paint*>(paintHandle); - if (radius <= 0) { - paint->setLooper(nullptr); - } - else { - SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); - paint->setLooper(SkBlurDrawLooper::Make((SkColor)color, sigma, dx, dy)); - } - } - static jboolean hasShadowLayer(jlong paintHandle) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr); @@ -1107,9 +1082,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}, + {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColor}, {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V", - (void*)PaintGlue::setShadowLayerLong}, + (void*)PaintGlue::setShadowLayer}, // --------------- @FastNative ---------------------- @@ -1139,9 +1114,6 @@ static const JNINativeMethod methods[] = { {"nSetDither","(JZ)V", (void*) PaintGlue::setDither}, {"nGetStyle","(J)I", (void*) PaintGlue::getStyle}, {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle}, - {"nGetColor","(J)I", (void*) PaintGlue::getColor}, - {"nSetColor","(JI)V", (void*) PaintGlue::setColor}, - {"nGetAlpha","(J)I", (void*) PaintGlue::getAlpha}, {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha}, {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth}, {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth}, @@ -1182,7 +1154,6 @@ static const JNINativeMethod methods[] = { {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness}, {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition}, {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness}, - {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer}, {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}, {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement}, }; diff --git a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp deleted file mode 100644 index 1c9ab9403298..000000000000 --- a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 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. - */ - -#define LOG_TAG "ActivityRecognitionHardware" - -#include <jni.h> -#include <nativehelper/JNIHelp.h> - -#include <android_runtime/AndroidRuntime.h> -#include <android_runtime/Log.h> - -// #include <hardware/activity_recognition.h> -// The activity recognition HAL is being deprecated. This means - -// i) Android framework code shall not depend on activity recognition -// being provided through the activity_recognition.h interface. -// ii) activity recognition HAL will not be binderized as the other HALs. -// - -/** - * Initializes the ActivityRecognitionHardware class from the native side. - */ -static void class_init(JNIEnv* /*env*/, jclass /*clazz*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); -} - -/** - * Initializes and connect the callbacks handlers in the HAL. - */ -static void initialize(JNIEnv* /*env*/, jobject /*obj*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); -} - -/** - * De-initializes the ActivityRecognitionHardware from the native side. - */ -static void release(JNIEnv* /*env*/, jobject /*obj*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); -} - -/** - * Returns true if ActivityRecognition HAL is supported, false otherwise. - */ -static jboolean is_supported(JNIEnv* /*env*/, jclass /*clazz*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); - return JNI_FALSE; -} - -/** - * Gets an array representing the supported activities. - */ -static jobjectArray get_supported_activities(JNIEnv* /*env*/, jobject /*obj*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); - return NULL; -} - -/** - * Enables a given activity event to be actively monitored. - */ -static int enable_activity_event( - JNIEnv* /*env*/, - jobject /*obj*/, - jint /*activity_handle*/, - jint /*event_type*/, - jlong /*report_latency_ns*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); - return android::NO_INIT; -} - -/** - * Disables a given activity event from being actively monitored. - */ -static int disable_activity_event( - JNIEnv* /*env*/, - jobject /*obj*/, - jint /*activity_handle*/, - jint /*event_type*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); - return android::NO_INIT; -} - -/** - * Request flush for al batch buffers. - */ -static int flush(JNIEnv* /*env*/, jobject /*obj*/) { - ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op", - __FUNCTION__); - return android::NO_INIT; -} - - -static const JNINativeMethod sMethods[] = { - // {"name", "signature", (void*) functionPointer }, - { "nativeClassInit", "()V", (void*) class_init }, - { "nativeInitialize", "()V", (void*) initialize }, - { "nativeRelease", "()V", (void*) release }, - { "nativeIsSupported", "()Z", (void*) is_supported }, - { "nativeGetSupportedActivities", "()[Ljava/lang/String;", (void*) get_supported_activities }, - { "nativeEnableActivityEvent", "(IIJ)I", (void*) enable_activity_event }, - { "nativeDisableActivityEvent", "(II)I", (void*) disable_activity_event }, - { "nativeFlush", "()I", (void*) flush }, -}; - -/** - * Registration method invoked in JNI load. - */ -int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env) { - return jniRegisterNativeMethods( - env, - "android/hardware/location/ActivityRecognitionHardware", - sMethods, - NELEM(sMethods)); -} 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_util_Process.cpp b/core/jni/android_util_Process.cpp index 798825fbe4a9..26a474cfecba 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -22,12 +22,12 @@ #include <utils/Log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> -#include <cutils/sched_policy.h> #include <utils/String8.h> #include <utils/Vector.h> #include <meminfo/procmeminfo.h> #include <meminfo/sysmeminfo.h> #include <processgroup/processgroup.h> +#include <processgroup/sched_policy.h> #include <string> #include <vector> 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/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index a8e142784397..7aee8338d3c4 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -14,6 +14,15 @@ * limitations under the License. */ +/* + * Disable optimization of this file if we are compiling with the address + * sanitizer. This is a mitigation for b/122921367 and can be removed once the + * bug is fixed. + */ +#if __has_feature(address_sanitizer) +#pragma clang optimize off +#endif + #define LOG_TAG "Zygote" // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc @@ -53,13 +62,13 @@ #include <android-base/stringprintf.h> #include <cutils/fs.h> #include <cutils/multiuser.h> -#include <cutils/sched_policy.h> #include <private/android_filesystem_config.h> #include <utils/String8.h> #include <selinux/android.h> #include <seccomp_policy.h> #include <stats_event_list.h> #include <processgroup/processgroup.h> +#include <processgroup/sched_policy.h> #include "core_jni_helpers.h" #include <nativehelper/JNIHelp.h> @@ -86,6 +95,7 @@ using android::base::GetBoolProperty; static pid_t gSystemServerPid = 0; static const char kIsolatedStorage[] = "persist.sys.isolated_storage"; +static const char kIsolatedStorageSnapshot[] = "sys.isolated_storage_snapshot"; static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkSystemServerHooks; @@ -303,7 +313,7 @@ static void PreApplicationInit() { mallopt(M_DECAY_TIME, 1); } -static void SetUpSeccompFilter(uid_t uid) { +static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) { if (!g_is_security_enforced) { ALOGI("seccomp disabled by setenforce 0"); return; @@ -311,7 +321,14 @@ static void SetUpSeccompFilter(uid_t uid) { // Apply system or app filter based on uid. if (uid >= AID_APP_START) { - set_app_seccomp_filter(); + if (is_child_zygote) { + // set_app_zygote_seccomp_filter(); + // TODO(b/111434506) install the filter; for now, install the app filter + // which is more restrictive. + set_app_seccomp_filter(); + } else { + set_app_seccomp_filter(); + } } else { set_system_seccomp_filter(); } @@ -530,7 +547,7 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, return true; } - if (GetBoolProperty(kIsolatedStorage, false)) { + if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) { if (mount_mode == MOUNT_EXTERNAL_FULL) { storageSource = "/mnt/runtime/write"; if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", @@ -996,7 +1013,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that // breaks SELinux domain transition (see b/71859146). As the result, // privileged syscalls used below still need to be accessible in app process. - SetUpSeccompFilter(uid); + SetUpSeccompFilter(uid, is_child_zygote); if (setresuid(uid, uid, uid) == -1) { fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno))); @@ -1295,6 +1312,23 @@ static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* en UnmountTree("/storage"); } +static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter( + JNIEnv* env, jclass, jint uidGidMin, jint uidGidMax) { + if (!g_is_security_enforced) { + ALOGI("seccomp disabled by setenforce 0"); + return; + } + + // TODO(b/111434506) install the filter + + /* + bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax); + if (!installed) { + RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter."); + } + */ +} + static const JNINativeMethod gMethods[] = { { "nativeSecurityInit", "()V", (void *) com_android_internal_os_Zygote_nativeSecurityInit }, @@ -1308,7 +1342,9 @@ static const JNINativeMethod gMethods[] = { { "nativeUnmountStorageOnInit", "()V", (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }, { "nativePreApplicationInit", "()V", - (void *) com_android_internal_os_Zygote_nativePreApplicationInit } + (void *) com_android_internal_os_Zygote_nativePreApplicationInit }, + { "nativeInstallSeccompUidGidFilter", "(II)V", + (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 2148273208e5..f68c760a9dbb 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; @@ -92,6 +2104,69 @@ 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; @@ -100,4 +2175,8 @@ enum PageId { // OPEN: Settings > System > Input & Gesture > Tap to check SETTINGS_GESTURE_TAP_SCREEN = 1626; + + // OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of + // SIM/eSIM subscriptions. + MOBILE_NETWORK_LIST = 1627; } diff --git a/core/proto/android/server/location/enums.proto b/core/proto/android/server/location/enums.proto index b6dc58932ce7..943ff181fe14 100644 --- a/core/proto/android/server/location/enums.proto +++ b/core/proto/android/server/location/enums.proto @@ -28,3 +28,105 @@ enum GpsSignalQualityEnum { GPS_SIGNAL_QUALITY_POOR = 0; GPS_SIGNAL_QUALITY_GOOD = 1; } + +// A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc. +enum GnssNiType { + VOICE = 1; + UMTS_SUPL = 2; + UMTS_CTRL_PLANE = 3; + EMERGENCY_SUPL = 4; +}; + +// GNSS NI responses, used to define the response in NI structures. +enum GnssUserResponseType { + RESPONSE_ACCEPT = 1; + RESPONSE_DENY = 2; + RESPONSE_NORESP = 3; +}; + +// GNSS NI data encoding scheme. +enum GnssNiEncodingType { + ENC_NONE = 0; + ENC_SUPL_GSM_DEFAULT = 1; + ENC_SUPL_UTF8 = 2; + ENC_SUPL_UCS2 = 3; + ENC_UNKNOWN = -1; +}; + +// Protocol stack that initiated the non-framework location request. +enum NfwProtocolStack { + // Cellular control plane requests. + CTRL_PLANE = 0; + // All types of SUPL requests. + SUPL = 1; + // All types of requests from IMS. + IMS = 10; + // All types of requests from SIM. + SIM = 11; + // Requests from other protocol stacks. + OTHER_PROTOCOL_STACK = 100; +}; + +// Source initiating/receiving the location information. +enum NfwRequestor { + // Wireless service provider. + CARRIER = 0; + // Device manufacturer. + OEM = 10; + // Modem chipset vendor. + MODEM_CHIPSET_VENDOR = 11; + // GNSS chipset vendor. + GNSS_CHIPSET_VENDOR = 12; + // Other chipset vendor. + OTHER_CHIPSET_VENDOR = 13; + // Automobile client. + AUTOMOBILE_CLIENT = 20; + // Other sources. + OTHER_REQUESTOR = 100; +}; + +// Indicates whether location information was provided for this request. +enum NfwResponseType { + // Request rejected because framework has not given permission for this use case. + REJECTED = 0; + // Request accepted but could not provide location because of a failure. + ACCEPTED_NO_LOCATION_PROVIDED = 1; + // Request accepted and location provided. + ACCEPTED_LOCATION_PROVIDED = 2; +}; + +// The SUPL mode. +enum SuplMode { + // Mobile Station Based. + MSB = 0x01; + // Mobile Station Assisted. + MSA = 0x02; +}; + +// Enum that hold the bit masks for various LTE Positioning Profile settings (LPP_PROFILE +// configuration parameter). If none of the bits in the enum are set, the default setting is +// Radio Resource Location Protocol(RRLP). +enum LppProfile { + // Enable LTE Positioning Protocol user plane. + USER_PLANE = 0x01; + // Enable LTE Positioning Protocol Control plane. + CONTROL_PLANE = 0x02; +}; + +// Positioning protocol on A-Glonass system. +enum GlonassPosProtocol { + // Radio Resource Control(RRC) control-plane. + RRC_CPLANE = 0x01; + // Radio Resource Location user-plane. + RRLP_CPLANE = 0x02; + // LTE Positioning Protocol User plane. + LPP_UPLANE = 0x04; +}; + +// Configurations of how GPS functionalities should be locked when user turns off GPS On setting. +enum GpsLock { + // Lock Mobile Originated GPS functionalitues. + MO = 0x01; + // Lock Network Initiated GPS functionalities. + NI = 0x02; +}; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7813128d46c7..ea0c8e250fe2 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" @@ -2209,8 +2210,9 @@ <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" android:protectionLevel="signature|installer" /> - <!-- @SystemApi Allows an application to start an activity within its managed profile from - the personal profile. + <!-- @SystemApi Allows an application to start its own activities, but on a different profile + associated with the user. For example, an application running on the main profile of a user + can start an activity on a managed profile of that user. This permission is not available to third party applications. @hide --> <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" @@ -2532,6 +2534,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 --> @@ -3045,6 +3057,15 @@ <permission android:name="android.permission.BIND_TEXT_SERVICE" android:protectionLevel="signature" /> + <!-- @SystemApi Must be required by a AttentionService + to ensure that only the system can bind to it. + <p>Protection level: signature + @hide + --> + <permission android:name="android.permission.BIND_ATTENTION_SERVICE" + android:protectionLevel="signature" /> + <uses-permission android:name="android.permission.BIND_ATTENTION_SERVICE" /> + <!-- Must be required by a {@link android.net.VpnService}, to ensure that only the system can bind to it. <p>Protection level: signature @@ -4744,6 +4765,11 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> + <service android:name="com.android.server.ZramWriteback" + android:exported="false" + android:permission="android.permission.BIND_JOB_SERVICE" > + </service> + <service android:name="com.android.server.backup.FullBackupJob" android:exported="true" android:permission="android.permission.BIND_JOB_SERVICE" > 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/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml deleted file mode 100644 index be5d9b436b79..000000000000 --- a/core/res/res/layout/notification_template_ambient_header.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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. - --> - -<!-- hack to work around <include /> not being supported at the top level --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:paddingStart="@dimen/notification_extra_margin_ambient" - android:paddingEnd="@dimen/notification_extra_margin_ambient" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <include - layout="@layout/notification_template_header" - android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/> -</FrameLayout> diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml deleted file mode 100644 index 2c6064ea6243..000000000000 --- a/core/res/res/layout/notification_template_material_ambient.xml +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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. - --> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/status_bar_latest_event_content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:tag="ambient" - android:paddingStart="@dimen/notification_extra_margin_ambient" - android:paddingEnd="@dimen/notification_extra_margin_ambient" - > - <include layout="@layout/notification_template_ambient_header" - android:theme="@style/Theme.DeviceDefault.Notification.Ambient" /> - - <LinearLayout - android:id="@+id/notification_action_list_margin_target" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top" - android:layout_marginTop="@dimen/notification_content_margin_top" - android:layout_marginBottom="@dimen/notification_action_list_height" - android:paddingTop="4dp" - android:paddingBottom="6dp" - android:clipToPadding="false" - android:orientation="vertical"> - - <LinearLayout - android:id="@+id/notification_main_column" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="top" - android:layout_weight="1" - android:paddingStart="@dimen/notification_content_margin_start" - android:paddingEnd="@dimen/notification_content_margin_end" - android:clipToPadding="false" - android:minHeight="@dimen/notification_min_content_height" - android:orientation="vertical" - > - <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="top|center_horizontal" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:textSize="@dimen/notification_ambient_title_text_size" - android:textColor="#ffffffff" - /> - <TextView android:id="@+id/text" - android:layout_width="match_parent" - android:layout_height="0dp" - android:textAppearance="@style/TextAppearance.DeviceDefault.Notification" - android:singleLine="false" - android:layout_weight="1" - android:gravity="top|center_horizontal" - android:visibility="gone" - android:textSize="@dimen/notification_ambient_text_size" - android:textColor="#eeffffff" - android:layout_marginTop="4dp" - android:ellipsize="end" - android:maxLines="3" - /> - </LinearLayout> - <FrameLayout android:id="@+id/actions_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom"> - <com.android.internal.widget.NotificationActionListLayout - android:id="@+id/actions" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_action_list_height" - android:paddingEnd="4dp" - android:orientation="horizontal" - android:gravity="center" - android:visibility="gone" - /> - </FrameLayout> - </LinearLayout> -</FrameLayout> 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 9a1360b18be8..c6f4ed05cdab 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> @@ -1741,19 +1740,6 @@ config_enableGeofenceOverlay is false. --> <string name="config_geofenceProviderPackageName" translatable="false">@null</string> - <!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware - Activity-Recognition to be replaced by an app at run-time. When disabled, only the - config_activityRecognitionHardwarePackageName package will be searched for - its implementation, otherwise packages whose signature matches the - signatures of config_locationProviderPackageNames will be searched, and - the service with the highest version number will be picked. Anyone who - wants to disable the overlay mechanism can set it to false. - --> - <bool name="config_enableActivityRecognitionHardwareOverlay" translatable="false">true</bool> - <!-- Package name providing Hardware Activity-Recognition API support. Used only when - config_enableActivityRecognitionHardwareOverlay is false. --> - <string name="config_activityRecognitionHardwarePackageName" translatable="false">@null</string> - <!-- Package name(s) containing location provider support. These packages can contain services implementing location providers, such as the Geocode Provider, Network Location Provider, and @@ -1892,6 +1878,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. @@ -1910,6 +1898,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> @@ -2805,6 +2799,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> @@ -3439,7 +3436,7 @@ See android.view.textclassifier.TextClassificationManager. --> <string name="config_defaultTextClassifierPackage" translatable="false"></string> - + <!-- The package name for the default wellbeing app. This package must be trusted, as it has the permissions to control other applications on the device. @@ -3447,6 +3444,12 @@ --> <string name="config_defaultWellbeingPackage" translatable="false"></string> + <!-- The component name for the default system attention service. + This service must be trusted, as it can be activated without explicit consent of the user. + See android.attention.AttentionManagerService. + --> + <string name="config_defaultAttentionService" translatable="false"></string> + <!-- The package name for the system's content capture service. This service must be trusted, as it can be activated without explicit consent of the user. If no service with the specified name exists on the device, content capture will be @@ -3719,4 +3722,7 @@ <!-- If the sensor that silences alerts is available or not. --> <bool name="config_silenceSensorAvailable">false</bool> + + <!-- Enable Zram writeback feature to allow unused pages in zram be written to flash. --> + <bool name="config_zramWriteback">false</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 05a156b69371..c8706838df57 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -194,9 +194,6 @@ <!-- The margin for text at the end of the image view for media notifications --> <dimen name="notification_media_image_margin_end">72dp</dimen> - <!-- The additional margin on the sides of the ambient view. --> - <dimen name="notification_extra_margin_ambient">16dp</dimen> - <!-- The height of the notification action list --> <dimen name="notification_action_list_height">60dp</dimen> @@ -233,9 +230,6 @@ <!-- The bottom padding for the notification header --> <dimen name="notification_header_padding_bottom">16dp</dimen> - <!-- The margin at the top of the notification header when dozing. --> - <dimen name="notification_header_margin_top_ambient">3dp</dimen> - <!-- The margin at the bottom of the notification header. --> <dimen name="notification_header_margin_bottom">0dp</dimen> @@ -400,11 +394,6 @@ <dimen name="notification_title_text_size">14sp</dimen> <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) --> <dimen name="notification_subtext_size">12sp</dimen> - <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) --> - <dimen name="notification_ambient_text_size">16sp</dimen> - <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) --> - <dimen name="notification_ambient_title_text_size">24sp</dimen> - <!-- Top padding for notifications in the standard layout. --> <dimen name="notification_top_pad">10dp</dimen> 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/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 200ef2f56f51..79afe697c504 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -271,9 +271,6 @@ easier. <style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> - <style name="TextAppearance.DeviceDefault.Notification.Info.Ambient" parent="TextAppearance.Material.Notification.Info.Ambient"> - <item name="fontFamily">@string/config_bodyFontFamily</item> - </style> <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 5a7199d6f445..63ac0e6bfc3e 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -487,10 +487,6 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" /> - <style name="TextAppearance.Material.Notification.Info.Ambient"> - <item name="textSize">@dimen/notification_text_size</item> - </style> - <style name="TextAppearance.Material.Notification.Emphasis"> <item name="textColor">#66000000</item> </style> @@ -1308,10 +1304,5 @@ please see styles_device_defaults.xml. <item name="gravity">top</item> </style> - <style name="Notification.Header.Ambient"> - <item name="layout_marginTop">@dimen/notification_header_margin_top_ambient</item> - <item name="gravity">top|center_horizontal</item> - </style> - </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cd1ee2b0166d..cc4599ec8f4c 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" /> @@ -1853,7 +1852,6 @@ <java-symbol type="bool" name="config_enableNightMode" /> <java-symbol type="bool" name="config_tintNotificationActionButtons" /> <java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" /> - <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" /> <java-symbol type="bool" name="config_enableFusedLocationOverlay" /> <java-symbol type="bool" name="config_enableHardwareFlpOverlay" /> <java-symbol type="bool" name="config_enableGeocoderOverlay" /> @@ -1880,6 +1878,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" /> @@ -2021,7 +2020,6 @@ <java-symbol type="string" name="car_mode_disable_notification_title" /> <java-symbol type="string" name="chooser_wallpaper" /> <java-symbol type="string" name="config_datause_iface" /> - <java-symbol type="string" name="config_activityRecognitionHardwarePackageName" /> <java-symbol type="string" name="config_fusedLocationProviderPackageName" /> <java-symbol type="string" name="config_hardwareFlpPackageName" /> <java-symbol type="string" name="config_geocoderProviderPackageName" /> @@ -2140,6 +2138,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" /> @@ -3075,8 +3074,6 @@ <java-symbol type="dimen" name="config_appTransitionAnimationDurationScaleDefault" /> - <java-symbol type="layout" name="notification_template_material_ambient" /> - <!-- Network Recommendation --> <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" /> @@ -3236,8 +3233,6 @@ <java-symbol type="string" name="time_picker_text_input_mode_description"/> <java-symbol type="string" name="time_picker_radial_mode_description"/> - <java-symbol type="layout" name="notification_template_ambient_header" /> - <!-- resolver activity --> <java-symbol type="drawable" name="resolver_icon_placeholder" /> @@ -3286,6 +3281,7 @@ <java-symbol type="string" name="config_defaultAugmentedAutofillService" /> <java-symbol type="string" name="config_defaultAppPredictionService" /> <java-symbol type="string" name="config_defaultContentSuggestionsService" /> + <java-symbol type="string" name="config_defaultAttentionService" /> <java-symbol type="string" name="notification_channel_foreground_service" /> <java-symbol type="string" name="foreground_service_app_in_background" /> @@ -3541,4 +3537,6 @@ <java-symbol type="bool" name="config_skipSensorAvailable" /> <java-symbol type="bool" name="config_silenceSensorAvailable" /> + + <java-symbol type="bool" name="config_zramWriteback" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index d60313a4402c..75a727bdcfdf 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1711,8 +1711,4 @@ easier. <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info</item> </style> - <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient"> - <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info.Ambient</item> - </style> - </resources> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index ccaf04160dc5..6b7698e344ad 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -1341,14 +1341,6 @@ please see themes_device_defaults.xml. <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item> </style> - <!-- Theme for inflating ambient notification --> - <style name="Theme.Material.Notification.Ambient"> - <item name="notificationHeaderStyle">@style/Notification.Header.Ambient</item> - <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item> - <item name="notificationHeaderAppNameVisibility">gone</item> - <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item> - </style> - <!-- Default theme for Settings and activities launched from Settings. --> <style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar"> <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item> diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk index 24f0cf07f443..6b0484ec366d 100644 --- a/core/tests/BroadcastRadioTests/Android.mk +++ b/core/tests/BroadcastRadioTests/Android.mk @@ -25,7 +25,7 @@ LOCAL_MODULE_TAGS := tests # LOCAL_SDK_VERSION := current LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test testng +LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util androidx.test.rules testng LOCAL_JAVA_LIBRARIES := android.test.base diff --git a/core/tests/BroadcastRadioTests/AndroidManifest.xml b/core/tests/BroadcastRadioTests/AndroidManifest.xml index d9b5522693e3..ce12cc99946f 100644 --- a/core/tests/BroadcastRadioTests/AndroidManifest.xml +++ b/core/tests/BroadcastRadioTests/AndroidManifest.xml @@ -24,7 +24,7 @@ </application> <instrumentation - android:name="android.support.test.runner.AndroidJUnitRunner" + android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.hardware.radio.tests" android:label="Tests for Broadcast Radio APIs" > </instrumentation> diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java index fdaba089490e..d2bd1e172ee2 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java @@ -15,22 +15,29 @@ */ package android.hardware.radio.tests.functional; +import static org.junit.Assert.*; +import static org.junit.Assume.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.testng.Assert.assertThrows; + import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioTuner; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; @@ -41,19 +48,10 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import static org.junit.Assert.*; -import static org.junit.Assume.*; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.after; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atMost; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.testng.Assert.assertThrows; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * A test for broadcast radio API. diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index e80cb6d0295b..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" /> diff --git a/core/tests/coretests/README b/core/tests/coretests/README index ea282a0ebb3a..34beb454b397 100644 --- a/core/tests/coretests/README +++ b/core/tests/coretests/README @@ -30,9 +30,9 @@ Next, install the resulting APK and run tests as you would normal JUnit tests: adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk adb shell am instrument -w \ - com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner -To run a tests within a specific package, add the following argument AFTER -w: +To run a tests within a specific package, add -e AFTER -w and before the runner class: -e package android.content.pm diff --git a/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml b/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml new file mode 100644 index 000000000000..8d630b09ef42 --- /dev/null +++ b/core/tests/coretests/res/color/drawable_in_color_dir_invalid.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. +--> + +<!-- This drawable is purposely in the color directory to test a backwards compatible fallback. --> +<layer-list + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/icon" android:drawable="@string/app_name" /> +</layer-list>
\ No newline at end of file diff --git a/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml b/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml new file mode 100644 index 000000000000..d1404753d57a --- /dev/null +++ b/core/tests/coretests/res/color/drawable_in_color_dir_valid.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. +--> + +<!-- This drawable is purposely in the color directory to test a backwards compatible fallback. --> +<layer-list + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/background" android:drawable="@drawable/test32x24" /> + <item android:id="@android:id/icon" android:drawable="@drawable/test16x12" /> +</layer-list>
\ No newline at end of file diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java new file mode 100644 index 000000000000..c4df88b49935 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java @@ -0,0 +1,81 @@ +/* + * 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.content.res; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.graphics.drawable.ColorStateListDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.coretests.R; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ResourcesDrawableTest { + + @Test + public void testLoadColorAsDrawable() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Resources resources = context.getResources(); + Drawable drawable = resources.getDrawable(R.color.color1); + assertTrue(drawable instanceof ColorStateListDrawable); + } + + @Test + public void testLoadColorAsDrawableFailureThrowsOriginalException() throws Throwable { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Resources resources = context.getResources(); + + Exception exception = null; + + try { + resources.getDrawable(R.color.drawable_in_color_dir_invalid); + } catch (Exception e) { + exception = e; + } + + assertNotNull( + "Loading drawable_in_color_dir_invalid should throw an exception", + exception + ); + + assertEquals( + "Can't find ColorStateList from drawable resource ID #0x" + + Integer.toHexString(R.color.drawable_in_color_dir_invalid), + exception.getCause().getCause().getMessage() + ); + } + + @Test + public void testLoadNormalDrawableInColorDir() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Resources resources = context.getResources(); + Drawable drawable = resources.getDrawable(R.color.drawable_in_color_dir_valid); + assertTrue(drawable instanceof LayerDrawable); + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 93af0130d74c..0fb1c7c7a7ec 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -199,6 +199,7 @@ public class SettingsBackupTest { Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD, Settings.Global.DATA_STALL_EVALUATION_TYPE, Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL, + Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD, Settings.Global.DEBUG_APP, Settings.Global.DEBUG_VIEW_ATTRIBUTES, @@ -312,6 +313,7 @@ public class SettingsBackupTest { Settings.Global.MDC_INITIAL_MAX_RETRY, Settings.Global.MHL_INPUT_SWITCHING_ENABLED, Settings.Global.MHL_POWER_CHARGE_ENABLED, + Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, Settings.Global.MOBILE_DATA, // Candidate for backup? Settings.Global.MOBILE_DATA_ALWAYS_ON, Settings.Global.MODE_RINGER, 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 2a962c222371..37fe22fb532d 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java @@ -45,6 +45,7 @@ public class KeyChainSnapshotTest { private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN; private static final String KEY_ALIAS = "steph"; private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 }; + private static final byte[] KEY_METADATA = new byte[] { 5, 3, 11, 13 }; private static final CertPath CERT_PATH = TestData.getThmCertPath(); @Test @@ -100,6 +101,7 @@ public class KeyChainSnapshotTest { WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0); assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias()); assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial()); + assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata()); } @Test @@ -166,6 +168,7 @@ public class KeyChainSnapshotTest { WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0); assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias()); assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial()); + assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata()); } private static KeyChainSnapshot createKeyChainSnapshot() throws Exception { @@ -197,6 +200,7 @@ public class KeyChainSnapshotTest { return new WrappedApplicationKey.Builder() .setAlias(KEY_ALIAS) .setEncryptedKeyMaterial(KEY_MATERIAL) + .setMetadata(KEY_METADATA) .build(); } 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 8522e6203284..aec54e190114 100644 --- a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java @@ -35,6 +35,7 @@ public class WrappedApplicationKeyTest { private static final String ALIAS = "karlin"; private static final byte[] KEY_MATERIAL = new byte[] { 0, 1, 2, 3, 4 }; + private static final byte[] METADATA = new byte[] {3, 2, 1, 0}; private Parcel mParcel; @@ -59,8 +60,18 @@ public class WrappedApplicationKeyTest { } @Test + public void build_setsMetadata_nonNull() { + assertArrayEquals(METADATA, buildTestKeyWithMetadata(METADATA).getMetadata()); + } + + @Test + public void build_setsMetadata_null() { + assertArrayEquals(null, buildTestKeyWithMetadata(null).getMetadata()); + } + + @Test public void writeToParcel_writesAliasToParcel() { - buildTestKey().writeToParcel(mParcel, /*flags=*/ 0); + buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0); mParcel.setDataPosition(0); WrappedApplicationKey readFromParcel = @@ -70,7 +81,7 @@ public class WrappedApplicationKeyTest { @Test public void writeToParcel_writesKeyMaterial() { - buildTestKey().writeToParcel(mParcel, /*flags=*/ 0); + buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0); mParcel.setDataPosition(0); WrappedApplicationKey readFromParcel = @@ -78,10 +89,48 @@ public class WrappedApplicationKeyTest { assertArrayEquals(KEY_MATERIAL, readFromParcel.getEncryptedKeyMaterial()); } + @Test + public void writeToParcel_writesMetadata_nonNull() { + buildTestKeyWithMetadata(METADATA).writeToParcel(mParcel, /*flags=*/ 0); + + mParcel.setDataPosition(0); + WrappedApplicationKey readFromParcel = + WrappedApplicationKey.CREATOR.createFromParcel(mParcel); + assertArrayEquals(METADATA, readFromParcel.getMetadata()); + } + + @Test + public void writeToParcel_writesMetadata_null() { + buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0); + + mParcel.setDataPosition(0); + WrappedApplicationKey readFromParcel = + WrappedApplicationKey.CREATOR.createFromParcel(mParcel); + assertArrayEquals(null, readFromParcel.getMetadata()); + } + + @Test + public void writeToParcel_writesMetadata_absent() { + buildTestKey().writeToParcel(mParcel, /*flags=*/ 0); + + mParcel.setDataPosition(0); + WrappedApplicationKey readFromParcel = + WrappedApplicationKey.CREATOR.createFromParcel(mParcel); + assertArrayEquals(null, readFromParcel.getMetadata()); + } + private WrappedApplicationKey buildTestKey() { return new WrappedApplicationKey.Builder() .setAlias(ALIAS) .setEncryptedKeyMaterial(KEY_MATERIAL) .build(); } + + private WrappedApplicationKey buildTestKeyWithMetadata(byte[] metadata) { + return new WrappedApplicationKey.Builder() + .setAlias(ALIAS) + .setEncryptedKeyMaterial(KEY_MATERIAL) + .setMetadata(metadata) + .build(); + } } diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index a2f0eba0b831..ff97aa1d3914 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -97,6 +97,22 @@ public class ContentCaptureSessionTest { assertThat(structure.getAutofillId()).isEqualTo(childId); } + @Test + public void testNotifyViewsDisappeared_invalid() { + // Null parent + assertThrows(NullPointerException.class, + () -> mSession1.notifyViewsDisappeared(null, new int[] {42})); + // Null child + assertThrows(IllegalArgumentException.class, + () -> mSession1.notifyViewsDisappeared(new AutofillId(42), null)); + // Empty child + assertThrows(IllegalArgumentException.class, + () -> mSession1.notifyViewsDisappeared(new AutofillId(42), new int[] {})); + // Virtual parent + assertThrows(IllegalArgumentException.class, + () -> mSession1.notifyViewsDisappeared(new AutofillId(42, 108), new int[] {666})); + } + // Cannot use @Spy because we need to pass the session id on constructor private class MyContentCaptureSession extends ContentCaptureSession { @@ -115,7 +131,7 @@ public class ContentCaptureSessionTest { } @Override - void flush() { + void flush(int reason) { throw new UnsupportedOperationException("should not have been called"); } @@ -135,7 +151,7 @@ public class ContentCaptureSessionTest { } @Override - void internalNotifyViewTextChanged(AutofillId id, CharSequence text, int flags) { + void internalNotifyViewTextChanged(AutofillId id, CharSequence text) { throw new UnsupportedOperationException("should not have been called"); } } 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/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk index d161059ed024..cc2d8d256e6a 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk @@ -38,16 +38,11 @@ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_MIN_SDK_VERSION := 8 -LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex \ - -D jack.dex.output.multidex.legacy=true - include $(BUILD_PACKAGE) -ifndef LOCAL_JACK_ENABLED -$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ echo "com/android/multidexlegacyandexception/Test.class" >> $@ $(built_dex_intermediate): $(mainDexList) -endif diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk index cf8fc92c7026..c577eeffd488 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk @@ -38,9 +38,9 @@ LOCAL_MIN_SDK_VERSION := 8 include $(BUILD_PACKAGE) -$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ echo "com/android/multidexlegacytestapp/Test.class" >> $@ $(built_dex_intermediate): $(mainDexList) @@ -69,9 +69,9 @@ LOCAL_MIN_SDK_VERSION := 8 include $(BUILD_PACKAGE) -$(mainDexList2): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList2): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ echo "com/android/multidexlegacytestapp/Test.class" >> $@ $(built_dex_intermediate): $(mainDexList2) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk index 2ce50b3b7583..da40940e92e9 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk @@ -30,14 +30,13 @@ mainDexList:= \ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex -LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true LOCAL_DEX_PREOPT := false include $(BUILD_PACKAGE) -$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ $(built_dex_intermediate): $(mainDexList) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk index 8b0c750a9efc..665e22d5a0bc 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk @@ -35,9 +35,9 @@ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex include $(BUILD_PACKAGE) -$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ $(built_dex_intermediate): $(mainDexList) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk index a36c9934d87f..c827fa80ebcd 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk @@ -35,9 +35,9 @@ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex include $(BUILD_PACKAGE) -$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ $(built_dex_intermediate): $(mainDexList) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk index 6b7418c1adb1..3d6ad7d1aa57 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk @@ -35,9 +35,9 @@ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex include $(BUILD_PACKAGE) -$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES) +$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) $(hide) mkdir -p $(dir $@) - $(MAINDEXCLASSES) $< 1>$@ + PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ $(built_dex_intermediate): $(mainDexList) diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index cbb780deac8a..54e1abcaf1b7 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -68,26 +68,27 @@ public class Paint { Paint.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_PAINT_SIZE); } - private ColorFilter mColorFilter; - private MaskFilter mMaskFilter; - private PathEffect mPathEffect; - private Shader mShader; + @ColorLong private long mColor; + private ColorFilter mColorFilter; + private MaskFilter mMaskFilter; + private PathEffect mPathEffect; + private Shader mShader; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private Typeface mTypeface; - private Xfermode mXfermode; + private Typeface mTypeface; + private Xfermode mXfermode; - private boolean mHasCompatScaling; - private float mCompatScaling; - private float mInvCompatScaling; + private boolean mHasCompatScaling; + private float mCompatScaling; + private float mInvCompatScaling; - private LocaleList mLocales; - private String mFontFeatureSettings; - private String mFontVariationSettings; + private LocaleList mLocales; + private String mFontFeatureSettings; + private String mFontVariationSettings; - private float mShadowLayerRadius; - private float mShadowLayerDx; - private float mShadowLayerDy; - private int mShadowLayerColor; + private float mShadowLayerRadius; + private float mShadowLayerDx; + private float mShadowLayerDy; + @ColorLong private long mShadowLayerColor; private static final Object sCacheLock = new Object(); @@ -505,6 +506,7 @@ public class Paint { // ? HINTING_OFF : HINTING_ON); mCompatScaling = mInvCompatScaling = 1; setTextLocales(LocaleList.getAdjustedDefault()); + mColor = Color.pack(Color.BLACK); } /** @@ -530,6 +532,7 @@ public class Paint { // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV // ? HINTING_OFF : HINTING_ON); + mColor = Color.pack(Color.BLACK); mColorFilter = null; mMaskFilter = null; mPathEffect = null; @@ -551,7 +554,7 @@ public class Paint { mShadowLayerRadius = 0.0f; mShadowLayerDx = 0.0f; mShadowLayerDy = 0.0f; - mShadowLayerColor = 0; + mShadowLayerColor = Color.pack(0); } /** @@ -572,6 +575,7 @@ public class Paint { * {@link Paint}. */ private void setClassVariablesFrom(Paint paint) { + mColor = paint.mColor; mColorFilter = paint.mColorFilter; mMaskFilter = paint.mMaskFilter; mPathEffect = paint.mPathEffect; @@ -949,7 +953,7 @@ public class Paint { } /** - * Return the paint's color. Note that the color is a 32bit value + * Return the paint's color in sRGB. Note that the color is a 32bit value * containing alpha as well as r,g,b. This 32bit value is not premultiplied, * meaning that its alpha can be any value, regardless of the values of * r,g,b. See the Color class for more details. @@ -958,7 +962,25 @@ public class Paint { */ @ColorInt public int getColor() { - return nGetColor(mNativePaint); + return Color.toArgb(mColor); + } + + /** + * Return the paint's color. 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. + * + * @see Color for APIs that help manipulate a color long. + * + * @return the paint's color (and alpha). + * + * @hide pending API approval + */ + @TestApi + @ColorLong + public long getColorLong() { + return mColor; } /** @@ -970,7 +992,7 @@ public class Paint { * @param color The new color (including alpha) to set in the paint. */ public void setColor(@ColorInt int color) { - nSetColor(mNativePaint, color); + setColor(Color.pack(color)); } /** @@ -996,6 +1018,7 @@ public class Paint { float a = Color.alpha(color); nSetColor(mNativePaint, cs, r, g, b, a); + mColor = color; } /** @@ -1006,7 +1029,7 @@ public class Paint { * @return the alpha component of the paint's color. */ public int getAlpha() { - return nGetAlpha(mNativePaint); + return Math.round(Color.alpha(mColor) * 255.0f); } /** @@ -1017,6 +1040,13 @@ public class Paint { * @param a set the alpha component [0..255] of the paint's color. */ public void setAlpha(int a) { + // FIXME: No need to unpack this. Instead, just update the alpha bits. + // b/122959599 + ColorSpace cs = Color.colorSpace(mColor); + float r = Color.red(mColor); + float g = Color.green(mColor); + float b = Color.blue(mColor); + mColor = Color.pack(r, g, b, a * (1.0f / 255), cs); nSetAlpha(mNativePaint, a); } @@ -1398,12 +1428,7 @@ public class Paint { * opaque, or the alpha from the shadow color if not. */ 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); + setShadowLayer(radius, dx, dy, Color.pack(shadowColor)); } /** @@ -1435,7 +1460,7 @@ public class Paint { mShadowLayerRadius = radius; mShadowLayerDx = dx; mShadowLayerDy = dy; - mShadowLayerColor = Color.toArgb(shadowColor); + mShadowLayerColor = shadowColor; } /** @@ -1482,8 +1507,20 @@ public class Paint { /** * Returns the color of the shadow layer. * @see #setShadowLayer(float,float,float,int) + * @see #setShadowLayer(float,float,float,long) */ public @ColorInt int getShadowLayerColor() { + return Color.toArgb(mShadowLayerColor); + } + + /** + * Returns the color of the shadow layer. + * @see #setShadowLayer(float,float,float,int) + * @see #setShadowLayer(float,float,float,long) + * @hide pending API approval + */ + @TestApi + public @ColorLong long getShadowLayerColorLong() { return mShadowLayerColor; } @@ -3074,12 +3111,6 @@ public class Paint { @CriticalNative private static native void nSetFilterBitmap(long paintPtr, boolean filter); @CriticalNative - private static native int nGetColor(long paintPtr); - @CriticalNative - private static native void nSetColor(long paintPtr, @ColorInt int color); - @CriticalNative - private static native int nGetAlpha(long paintPtr); - @CriticalNative private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText); @CriticalNative private static native boolean nIsElegantTextHeight(long paintPtr); 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/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index e6e6b0e62305..cfbb9956d3dc 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -100,7 +100,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con mSurfaceColorSpace, &props)); SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); layerUpdateQueue->clear(); // Draw visual debugging features diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index d7faaf707aa8..df8224372ea7 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -312,7 +312,8 @@ void SkiaPipeline::endCapture(SkSurface* surface) { void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - const Rect& contentDrawBounds, sk_sp<SkSurface> surface) { + const Rect& contentDrawBounds, sk_sp<SkSurface> surface, + const SkMatrix& preTransform) { renderVectorDrawableCache(); // draw all layers up front @@ -323,12 +324,12 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli std::unique_ptr<SkPictureRecorder> recorder; SkCanvas* canvas = tryCapture(surface.get()); - renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas); + renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas, preTransform); endCapture(surface.get()); if (CC_UNLIKELY(Properties::debugOverdraw)) { - renderOverdraw(layers, clip, nodes, contentDrawBounds, surface); + renderOverdraw(layers, clip, nodes, contentDrawBounds, surface, preTransform); } ATRACE_NAME("flush commands"); @@ -344,9 +345,11 @@ static Rect nodeBounds(RenderNode& node) { void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - const Rect& contentDrawBounds, SkCanvas* canvas) { + const Rect& contentDrawBounds, SkCanvas* canvas, + const SkMatrix& preTransform) { SkAutoCanvasRestore saver(canvas, true); - canvas->androidFramework_setDeviceClipRestriction(clip.roundOut()); + canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut()); + canvas->concat(preTransform); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) { @@ -486,7 +489,8 @@ static const uint32_t kOverdrawColors[2][6] = { void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, - const Rect& contentDrawBounds, sk_sp<SkSurface> surface) { + const Rect& contentDrawBounds, sk_sp<SkSurface> surface, + const SkMatrix& preTransform) { // Set up the overdraw canvas. SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo); @@ -496,7 +500,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& // each time a pixel would have been drawn. // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero // initialized. - renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas); + renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform); sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); // Draw overdraw colors to the canvas. The color filter will convert counts to colors. diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 94a699bfee31..cf6f5b284d8c 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -53,7 +53,8 @@ public: void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - const Rect& contentDrawBounds, sk_sp<SkSurface> surface); + const Rect& contentDrawBounds, sk_sp<SkSurface> surface, + const SkMatrix& preTransform); std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; } @@ -116,7 +117,8 @@ protected: private: void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - const Rect& contentDrawBounds, SkCanvas* canvas); + const Rect& contentDrawBounds, SkCanvas* canvas, + const SkMatrix& preTransform); /** * Debugging feature. Draws a semi-transparent overlay on each pixel, indicating @@ -124,7 +126,7 @@ private: */ void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds, - sk_sp<SkSurface>); + sk_sp<SkSurface> surface, const SkMatrix& preTransform); /** * Render mVectorDrawables into offscreen buffers. diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index b9aae9839bdc..53495a7d62c0 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -58,7 +58,8 @@ Frame SkiaVulkanPipeline::getFrame() { return Frame(-1, -1, 0); } - Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface)); + Frame frame(mVkSurface->windowWidth(), mVkSurface->windowHeight(), + mVkManager.getAge(mVkSurface)); return frame; } @@ -73,7 +74,8 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con return false; } SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, + backBuffer, mVkSurface->preTransform()); ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); layerUpdateQueue->clear(); diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 6c540f627a68..5c6cb9ad43db 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -480,6 +480,32 @@ VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurfa return backbuffer; } +static SkMatrix getPreTransformMatrix(int width, int height, + VkSurfaceTransformFlagBitsKHR transform) { + switch (transform) { + case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: + return SkMatrix::I(); + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1); + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1); + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1); + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: + return SkMatrix::MakeAll(-1, 0, width, 0, 1, 0, 0, 0, 1); + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: + return SkMatrix::MakeAll(0, -1, height, -1, 0, width, 0, 0, 1); + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: + return SkMatrix::MakeAll(1, 0, 0, 0, -1, height, 0, 0, 1); + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: + return SkMatrix::MakeAll(0, 1, 0, 1, 0, 0, 0, 0, 1); + default: + LOG_ALWAYS_FATAL("Unsupported pre transform of swapchain."); + } + return SkMatrix::I(); +} + + SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { // Recreate VulkanSurface, if ANativeWindow has been resized. VulkanSurface* surface = *surfaceOut; @@ -516,7 +542,7 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { // maybe use attach somehow? but need a Window return nullptr; } - if (VK_ERROR_OUT_OF_DATE_KHR == res) { + if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_SUBOPTIMAL_KHR == res) { // tear swapchain down and try again if (!createSwapchain(surface)) { return nullptr; @@ -595,6 +621,10 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { } backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + surface->mPreTransform = getPreTransformMatrix(surface->windowWidth(), + surface->windowHeight(), + surface->mTransform); + surface->mBackbuffer = std::move(skSurface); return surface->mBackbuffer.get(); } @@ -749,6 +779,17 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { return false; } + if (!SkToBool(caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) { + return false; + } + VkSurfaceTransformFlagBitsKHR transform; + if (SkToBool(caps.supportedTransforms & caps.currentTransform) && + !SkToBool(caps.currentTransform & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) { + transform = caps.currentTransform; + } else { + transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } + VkExtent2D extent = caps.currentExtent; // clamp width; to handle currentExtent of -1 and protect us from broken hints if (extent.width < caps.minImageExtent.width) { @@ -760,6 +801,16 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { extent.height = caps.minImageExtent.height; } SkASSERT(extent.height <= caps.maxImageExtent.height); + + VkExtent2D swapExtent = extent; + if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || + transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR || + transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR || + transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) { + swapExtent.width = extent.height; + swapExtent.height = extent.width; + } + surface->mWindowWidth = extent.width; surface->mWindowHeight = extent.height; @@ -775,7 +826,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags); - SkASSERT(caps.supportedTransforms & caps.currentTransform); + SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)); VkCompositeAlphaFlagBitsKHR composite_alpha = @@ -822,7 +873,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { swapchainCreateInfo.minImageCount = imageCount; swapchainCreateInfo.imageFormat = surfaceFormat; swapchainCreateInfo.imageColorSpace = colorSpace; - swapchainCreateInfo.imageExtent = extent; + swapchainCreateInfo.imageExtent = swapExtent; swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageUsage = usageFlags; @@ -837,7 +888,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { swapchainCreateInfo.pQueueFamilyIndices = nullptr; } - swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchainCreateInfo.preTransform = transform; swapchainCreateInfo.compositeAlpha = composite_alpha; swapchainCreateInfo.presentMode = mode; swapchainCreateInfo.clipped = true; @@ -848,6 +899,8 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { return false; } + surface->mTransform = transform; + // destroy the old swapchain if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) { mDeviceWaitIdle(mDevice); @@ -857,7 +910,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr); } - createBuffers(surface, surfaceFormat, extent); + createBuffers(surface, surfaceFormat, swapExtent); // The window content is not updated (frozen) until a buffer of the window size is received. // This prevents temporary stretching of the window after it is resized, but before the first diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 105ee093a057..b06eb82dac79 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -45,6 +45,14 @@ public: sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; } + // The width and height are are the logical width and height for when submitting draws to the + // surface. In reality if the window is rotated the underlying VkImage may have the width and + // height swapped. + int windowWidth() const { return mWindowWidth; } + int windowHeight() const { return mWindowHeight; } + + SkMatrix& preTransform() { return mPreTransform; } + private: friend class VulkanManager; struct BackbufferInfo { @@ -84,6 +92,8 @@ private: sk_sp<SkColorSpace> mColorSpace; SkColorSpace::Gamut mColorGamut; SkColorType mColorType; + VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + SkMatrix mPreTransform; }; // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 3c06dab36fe4..e86cf42fee4d 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -51,7 +51,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { auto surface = SkSurface::MakeRasterN32Premul(1, 1); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -83,7 +84,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -104,10 +106,12 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); } @@ -126,7 +130,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED); @@ -198,32 +203,38 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // Single draw, should be white. - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); // 1 Overdraw, should be blue blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff); // 2 Overdraw, should be green blended onto white renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0); // 3 Overdraw, should be pink blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0); // 4 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); // 5 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); } @@ -308,7 +319,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { SkRect dirty = SkRect::MakeWH(800, 600); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface, + SkMatrix::I()); EXPECT_EQ(4, surface->canvas()->mDrawCounter); } @@ -339,7 +351,43 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>()); pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, - SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface, SkMatrix::I()); + EXPECT_EQ(1, surface->canvas()->mDrawCounter); +} + +// Test renderFrame with a dirty clip and a pre-transform matrix. +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) { + static const int CANVAS_WIDTH = 200; + static const int CANVAS_HEIGHT = 100; + static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1); + static const SkRect dirty = SkRect::MakeLTRB(10, 20, 20, 40); + class ClippedTestCanvas : public SkCanvas { + public: + ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {} + void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override { + EXPECT_EQ(0, mDrawCounter++); + // Expect clip to be rotated. + EXPECT_EQ(SkRect::MakeLTRB(CANVAS_HEIGHT - dirty.fTop - dirty.height(), dirty.fLeft, + CANVAS_HEIGHT - dirty.fTop, dirty.fLeft + dirty.width()), + TestUtils::getClipBounds(this)); + EXPECT_EQ(rotateMatrix, getTotalMatrix()); + } + int mDrawCounter = 0; + }; + + std::vector<sp<RenderNode>> nodes; + nodes.push_back(TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(CANVAS_WIDTH, CANVAS_HEIGHT)); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + })); + + LayerUpdateQueue layerUpdateQueue; + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>()); + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface, rotateMatrix); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } @@ -369,7 +417,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>()); pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, - SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface, SkMatrix::I()); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index b4f19c99c6fe..d742cc34b57e 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -255,7 +255,7 @@ void PointerController::setPresentation(Presentation presentation) { if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) { mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources); + &mLocked.animationResources, mLocked.viewport.displayId); } if (mLocked.presentation != presentation) { @@ -727,14 +727,14 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() { } void PointerController::loadResourcesLocked() REQUIRES(mLock) { - mPolicy->loadPointerResources(&mResources); + mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); if (mLocked.presentation == PRESENTATION_POINTER) { mLocked.additionalMouseResources.clear(); mLocked.animationResources.clear(); - mPolicy->loadPointerIcon(&mLocked.pointerIcon); + mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources); + &mLocked.animationResources, mLocked.viewport.displayId); } mLocked.pointerIconChanged = true; diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index a32cc42a3342..be057867890d 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -62,10 +62,10 @@ protected: virtual ~PointerControllerPolicyInterface() { } public: - virtual void loadPointerIcon(SpriteIcon* icon) = 0; - virtual void loadPointerResources(PointerResources* outResources) = 0; + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, - std::map<int32_t, PointerAnimation>* outAnimationResources) = 0; + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; virtual int32_t getDefaultPointerIconId() = 0; virtual int32_t getCustomPointerIconId() = 0; }; diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java index 6c757f949f19..3922d2f9f558 100644 --- a/location/java/android/location/GnssSingleSatCorrection.java +++ b/location/java/android/location/GnssSingleSatCorrection.java @@ -16,11 +16,14 @@ package android.location; +import android.annotation.FloatRange; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + /** * A container with measurement corrections for a single visible satellite * @@ -31,9 +34,9 @@ public final class GnssSingleSatCorrection implements Parcelable { /** * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link - * #mSatIsLos}. + * #mProbSatIsLos}. */ - public static final int HAS_SAT_IS_LOS_MASK = 1 << 0; + public static final int HAS_PROB_SAT_IS_LOS_MASK = 1 << 0; /** * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link @@ -78,9 +81,11 @@ public final class GnssSingleSatCorrection implements Parcelable { private float mCarrierFrequencyHz; /** - * True if the satellite is estimated to be in Line-of-Sight condition at the given location. + * The probability that the satellite is estimated to be in Line-of-Sight condition at the given + * location. */ - private boolean mSatIsLos; + @FloatRange(from = 0f, to = 1f) + private float mProbSatIsLos; /** * Excess path length to be subtracted from pseudorange before using it in calculating location. @@ -103,7 +108,7 @@ public final class GnssSingleSatCorrection implements Parcelable { mSatId = builder.mSatId; mConstellationType = builder.mConstellationType; mCarrierFrequencyHz = builder.mCarrierFrequencyHz; - mSatIsLos = builder.mSatIsLos; + mProbSatIsLos = builder.mProbSatIsLos; mExcessPathLengthMeters = builder.mExcessPathLengthMeters; mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters; mReflectingPlane = builder.mReflectingPlane; @@ -152,9 +157,13 @@ public final class GnssSingleSatCorrection implements Parcelable { return mCarrierFrequencyHz; } - /** True if the satellite is line-of-sight */ - public boolean isSatelliteLineOfSight() { - return mSatIsLos; + /** + * Returns the probability that the satellite is in line-of-sight condition at the given + * location. + */ + @FloatRange(from = 0f, to = 1f) + public float getProbSatIsLos() { + return mProbSatIsLos; } /** @@ -180,9 +189,9 @@ public final class GnssSingleSatCorrection implements Parcelable { return mReflectingPlane; } - /** Returns {@code true} if {@link #isSatelliteLineOfSight()} is valid. */ + /** Returns {@code true} if {@link #getProbSatIsLos()} is valid. */ public boolean hasSatelliteLineOfSight() { - return (mSingleSatCorrectionFlags & HAS_SAT_IS_LOS_MASK) != 0; + return (mSingleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0; } /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */ @@ -215,7 +224,7 @@ public final class GnssSingleSatCorrection implements Parcelable { .setConstellationType(parcel.readInt()) .setSatId(parcel.readInt()) .setCarrierFrequencyHz(parcel.readFloat()) - .setSatIsLos(parcel.readBoolean()) + .setProbSatIsLos(parcel.readFloat()) .setExcessPathLengthMeters(parcel.readFloat()) .setExcessPathLengthUncertaintyMeters(parcel.readFloat()) .setReflectingPlane( @@ -239,7 +248,7 @@ public final class GnssSingleSatCorrection implements Parcelable { builder.append(String.format(format, "ConstellationType = ", mConstellationType)); builder.append(String.format(format, "SatId = ", mSatId)); builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz)); - builder.append(String.format(format, "SatIsLos = ", mSatIsLos)); + builder.append(String.format(format, "ProbSatIsLos = ", mProbSatIsLos)); builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters)); builder.append( String.format( @@ -256,7 +265,7 @@ public final class GnssSingleSatCorrection implements Parcelable { parcel.writeInt(mConstellationType); parcel.writeInt(mSatId); parcel.writeFloat(mCarrierFrequencyHz); - parcel.writeBoolean(mSatIsLos); + parcel.writeFloat(mProbSatIsLos); parcel.writeFloat(mExcessPathLengthMeters); parcel.writeFloat(mExcessPathLengthUncertaintyMeters); mReflectingPlane.writeToParcel(parcel, flags); @@ -274,7 +283,7 @@ public final class GnssSingleSatCorrection implements Parcelable { private int mConstellationType; private int mSatId; private float mCarrierFrequencyHz; - private boolean mSatIsLos; + private float mProbSatIsLos; private float mExcessPathLengthMeters; private float mExcessPathLengthUncertaintyMeters; private GnssReflectingPlane mReflectingPlane; @@ -303,10 +312,16 @@ public final class GnssSingleSatCorrection implements Parcelable { return this; } - /** Sets the line=of-sight state of the satellite */ - public Builder setSatIsLos(boolean satIsLos) { - mSatIsLos = satIsLos; - mSingleSatCorrectionFlags = (byte) (mSingleSatCorrectionFlags | HAS_SAT_IS_LOS_MASK); + /** + * Sets the line-of-sight probability of the satellite at the given location in the range + * between 0 and 1. + */ + public Builder setProbSatIsLos(@FloatRange(from = 0f, to = 1f) float probSatIsLos) { + Preconditions.checkArgumentInRange(probSatIsLos, 0, 1, + "probSatIsLos should be between 0 and 1."); + mProbSatIsLos = probSatIsLos; + mSingleSatCorrectionFlags = + (byte) (mSingleSatCorrectionFlags | HAS_PROB_SAT_IS_LOS_MASK); return this; } diff --git a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java deleted file mode 100644 index 843dd670315a..000000000000 --- a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 com.android.location.provider; - -import android.annotation.NonNull; - -import java.security.InvalidParameterException; -import java.util.List; - -/** - * A class representing an event for Activity changes. - * @hide - */ -public class ActivityChangedEvent { - private final List<ActivityRecognitionEvent> mActivityRecognitionEvents; - - public ActivityChangedEvent(List<ActivityRecognitionEvent> activityRecognitionEvents) { - if (activityRecognitionEvents == null) { - throw new InvalidParameterException( - "Parameter 'activityRecognitionEvents' must not be null."); - } - - mActivityRecognitionEvents = activityRecognitionEvents; - } - - @NonNull - public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() { - return mActivityRecognitionEvents; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:"); - - for (ActivityRecognitionEvent event : mActivityRecognitionEvents) { - builder.append("\n "); - builder.append(event.toString()); - } - builder.append("\n]"); - - return builder.toString(); - } -} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java deleted file mode 100644 index e54dea40d690..000000000000 --- a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 com.android.location.provider; - -/** - * A class that represents an Activity Recognition Event. - * @hide - */ -public class ActivityRecognitionEvent { - private final String mActivity; - private final int mEventType; - private final long mTimestampNs; - - public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) { - mActivity = activity; - mEventType = eventType; - mTimestampNs = timestampNs; - } - - public String getActivity() { - return mActivity; - } - - public int getEventType() { - return mEventType; - } - - public long getTimestampNs() { - return mTimestampNs; - } - - @Override - public String toString() { - String eventString; - switch (mEventType) { - case ActivityRecognitionProvider.EVENT_TYPE_ENTER: - eventString = "Enter"; - break; - case ActivityRecognitionProvider.EVENT_TYPE_EXIT: - eventString = "Exit"; - break; - case ActivityRecognitionProvider.EVENT_TYPE_FLUSH_COMPLETE: - eventString = "FlushComplete"; - break; - default: - eventString = "<Invalid>"; - break; - } - - return String.format( - "Activity='%s', EventType=%s(%s), TimestampNs=%s", - mActivity, - eventString, - mEventType, - mTimestampNs); - } -} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java deleted file mode 100644 index 0eff7d3f2014..000000000000 --- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 com.android.location.provider; - -import com.android.internal.util.Preconditions; - -import android.hardware.location.IActivityRecognitionHardware; -import android.hardware.location.IActivityRecognitionHardwareSink; -import android.os.RemoteException; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; - -/** - * A class that exposes {@link IActivityRecognitionHardware} functionality to unbundled services. - * @hide - */ -public final class ActivityRecognitionProvider { - private final IActivityRecognitionHardware mService; - private final HashSet<Sink> mSinkSet = new HashSet<>(); - - // the following constants must remain in sync with activity_recognition.h - - public static final String ACTIVITY_IN_VEHICLE = "android.activity_recognition.in_vehicle"; - public static final String ACTIVITY_ON_BICYCLE = "android.activity_recognition.on_bicycle"; - public static final String ACTIVITY_WALKING = "android.activity_recognition.walking"; - public static final String ACTIVITY_RUNNING = "android.activity_recognition.running"; - public static final String ACTIVITY_STILL = "android.activity_recognition.still"; - public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting"; - - // NOTE: when adding an additional EVENT_TYPE_, EVENT_TYPE_COUNT needs to be updated in - // android.hardware.location.ActivityRecognitionHardware - public static final int EVENT_TYPE_FLUSH_COMPLETE = 0; - public static final int EVENT_TYPE_ENTER = 1; - public static final int EVENT_TYPE_EXIT = 2; - - // end constants activity_recognition.h - - /** - * Used to receive Activity-Recognition events. - */ - public interface Sink { - void onActivityChanged(ActivityChangedEvent event); - } - - public ActivityRecognitionProvider(IActivityRecognitionHardware service) - throws RemoteException { - Preconditions.checkNotNull(service); - mService = service; - mService.registerSink(new SinkTransport()); - } - - public String[] getSupportedActivities() throws RemoteException { - return mService.getSupportedActivities(); - } - - public boolean isActivitySupported(String activity) throws RemoteException { - return mService.isActivitySupported(activity); - } - - public void registerSink(Sink sink) { - Preconditions.checkNotNull(sink); - synchronized (mSinkSet) { - mSinkSet.add(sink); - } - } - - // TODO: if this functionality is exposed to 3rd party developers, handle unregistration (here - // and in the service) of all sinks while failing to disable all events - public void unregisterSink(Sink sink) { - Preconditions.checkNotNull(sink); - synchronized (mSinkSet) { - mSinkSet.remove(sink); - } - } - - public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) - throws RemoteException { - return mService.enableActivityEvent(activity, eventType, reportLatencyNs); - } - - public boolean disableActivityEvent(String activity, int eventType) throws RemoteException { - return mService.disableActivityEvent(activity, eventType); - } - - public boolean flush() throws RemoteException { - return mService.flush(); - } - - private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub { - @Override - public void onActivityChanged(android.hardware.location.ActivityChangedEvent event) { - Collection<Sink> sinks; - synchronized (mSinkSet) { - if (mSinkSet.isEmpty()) { - return; - } - sinks = new ArrayList<>(mSinkSet); - } - - // translate the event from platform internal and GmsCore types - ArrayList<ActivityRecognitionEvent> gmsEvents = new ArrayList<>(); - for (android.hardware.location.ActivityRecognitionEvent reportingEvent - : event.getActivityRecognitionEvents()) { - ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent( - reportingEvent.getActivity(), - reportingEvent.getEventType(), - reportingEvent.getTimestampNs()); - gmsEvents.add(gmsEvent); - } - ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents); - - for (Sink sink : sinks) { - sink.onActivityChanged(gmsEvent); - } - } - } -} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java deleted file mode 100644 index 326d901b9daa..000000000000 --- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 - */ - -package com.android.location.provider; - -import android.annotation.NonNull; -import android.hardware.location.IActivityRecognitionHardware; -import android.hardware.location.IActivityRecognitionHardwareClient; -import android.os.Binder; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.util.Log; - -/** - * A client class for interaction with an Activity-Recognition provider. - * @hide - */ -public abstract class ActivityRecognitionProviderClient { - private static final String TAG = "ArProviderClient"; - - protected ActivityRecognitionProviderClient() {} - - private IActivityRecognitionHardwareClient.Stub mClient = - new IActivityRecognitionHardwareClient.Stub() { - @Override - public void onAvailabilityChanged( - boolean isSupported, - IActivityRecognitionHardware instance) { - int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SYSTEM_UID) { - Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid); - return; - } - ActivityRecognitionProvider provider; - try { - provider = isSupported ? new ActivityRecognitionProvider(instance) : null; - } catch (RemoteException e) { - Log.e(TAG, "Error creating Hardware Activity-Recognition Provider.", e); - return; - } - onProviderChanged(isSupported, provider); - } - }; - - /** - * Gets the binder needed to interact with proxy provider in the platform. - */ - @NonNull - public IBinder getBinder() { - return mClient; - } - - /** - * Called when a change in the availability of {@link ActivityRecognitionProvider} is detected. - * - * @param isSupported whether the platform supports the provider natively - * @param instance the available provider's instance - */ - public abstract void onProviderChanged( - boolean isSupported, - ActivityRecognitionProvider instance); -} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java deleted file mode 100644 index 42f77b42766f..000000000000 --- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 com.android.location.provider; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.hardware.location.IActivityRecognitionHardware; -import android.hardware.location.IActivityRecognitionHardwareWatcher; -import android.os.Binder; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.util.Log; - -/** - * A watcher class for Activity-Recognition instances. - * - * @deprecated use {@link ActivityRecognitionProviderClient} instead. - * @hide - */ -@Deprecated -public class ActivityRecognitionProviderWatcher { - private static final String TAG = "ActivityRecognitionProviderWatcher"; - - private static ActivityRecognitionProviderWatcher sWatcher; - private static final Object sWatcherLock = new Object(); - - private ActivityRecognitionProvider mActivityRecognitionProvider; - - private ActivityRecognitionProviderWatcher() {} - - public static ActivityRecognitionProviderWatcher getInstance() { - synchronized (sWatcherLock) { - if (sWatcher == null) { - sWatcher = new ActivityRecognitionProviderWatcher(); - } - return sWatcher; - } - } - - private IActivityRecognitionHardwareWatcher.Stub mWatcherStub = - new IActivityRecognitionHardwareWatcher.Stub() { - @Override - public void onInstanceChanged(IActivityRecognitionHardware instance) { - int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SYSTEM_UID) { - Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid); - return; - } - - try { - mActivityRecognitionProvider = new ActivityRecognitionProvider(instance); - } catch (RemoteException e) { - Log.e(TAG, "Error creating Hardware Activity-Recognition", e); - } - } - }; - - /** - * Gets the binder needed to interact with proxy provider in the platform. - */ - @NonNull - public IBinder getBinder() { - return mWatcherStub; - } - - /** - * Gets an object that supports the functionality of {@link ActivityRecognitionProvider}. - * - * @return Non-null value if the functionality is supported by the platform, false otherwise. - */ - @Nullable - public ActivityRecognitionProvider getActivityRecognitionProvider() { - return mActivityRecognitionProvider; - } -} diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java index c18d58f9a704..d6227bbfcd0d 100644 --- a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java +++ b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java @@ -59,7 +59,7 @@ public class GnssMeasurementCorrectionsTest extends TestCase { assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType()); assertEquals(11, singleSatCorrection.getSatId()); assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz()); - assertEquals(false, singleSatCorrection.isSatelliteLineOfSight()); + assertEquals(0.9f, singleSatCorrection.getProbSatIsLos()); assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters()); assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters()); GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane(); @@ -88,7 +88,7 @@ public class GnssMeasurementCorrectionsTest extends TestCase { .setConstellationType(GnssStatus.CONSTELLATION_GPS) .setSatId(11) .setCarrierFrequencyHz(1575430000f) - .setSatIsLos(false) + .setProbSatIsLos(0.9f) .setExcessPathLengthMeters(50.0f) .setExcessPathLengthUncertaintyMeters(55.0f) .setReflectingPlane(generateTestReflectingPlane()); diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java index 2e54ae463595..f358806d0b26 100644 --- a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java +++ b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java @@ -44,7 +44,7 @@ public class GnssSingleSatCorrectionsTest extends TestCase { assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType()); assertEquals(12, singleSatCorrection.getSatId()); assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz()); - assertEquals(true, singleSatCorrection.isSatelliteLineOfSight()); + assertEquals(0.1f, singleSatCorrection.getProbSatIsLos()); assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters()); assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters()); GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane(); @@ -58,7 +58,7 @@ public class GnssSingleSatCorrectionsTest extends TestCase { .setConstellationType(singleSatCorr.getConstellationType()) .setSatId(singleSatCorr.getSatId()) .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz()) - .setSatIsLos(singleSatCorr.isSatelliteLineOfSight()) + .setProbSatIsLos(singleSatCorr.getProbSatIsLos()) .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters()) .setExcessPathLengthUncertaintyMeters( singleSatCorr.getExcessPathLengthUncertaintyMeters()) @@ -72,7 +72,7 @@ public class GnssSingleSatCorrectionsTest extends TestCase { .setConstellationType(GnssStatus.CONSTELLATION_GALILEO) .setSatId(12) .setCarrierFrequencyHz(1575420000f) - .setSatIsLos(true) + .setProbSatIsLos(0.1f) .setExcessPathLengthMeters(10.0f) .setExcessPathLengthUncertaintyMeters(5.0f) .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane()); 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/AudioManager.java b/media/java/android/media/AudioManager.java index 30b5480fba55..202a3cf1d930 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3802,6 +3802,12 @@ public class AudioManager { public static final int DEVICE_IN_HDMI = AudioSystem.DEVICE_IN_HDMI; /** @hide + * The audio input device code for HDMI ARC + */ + public static final int DEVICE_IN_HDMI_ARC = + AudioSystem.DEVICE_IN_HDMI_ARC; + + /** @hide * The audio input device code for telephony voice RX path */ public static final int DEVICE_IN_TELEPHONY_RX = 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/AudioSystem.java b/media/java/android/media/AudioSystem.java index de73649d59dc..6b7447901cec 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -542,6 +542,7 @@ public class AudioSystem 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_HDMI_ARC = DEVICE_BIT_IN | 0x8000000; 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; @@ -570,6 +571,7 @@ public class AudioSystem DEVICE_IN_PROXY | DEVICE_IN_USB_HEADSET | DEVICE_IN_BLUETOOTH_BLE | + DEVICE_IN_HDMI_ARC | DEVICE_IN_ECHO_REFERENCE | DEVICE_IN_DEFAULT); public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET; @@ -647,6 +649,7 @@ public class AudioSystem 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"; + public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc"; @UnsupportedAppUsage public static String getOutputDeviceName(int device) @@ -767,6 +770,8 @@ public class AudioSystem return DEVICE_IN_BLUETOOTH_BLE_NAME; case DEVICE_IN_ECHO_REFERENCE: return DEVICE_IN_ECHO_REFERENCE_NAME; + case DEVICE_IN_HDMI_ARC: + return DEVICE_IN_HDMI_ARC_NAME; case DEVICE_IN_DEFAULT: default: return Integer.toString(device); diff --git a/media/java/android/media/Controller2Link.java b/media/java/android/media/Controller2Link.java index a62db5f1fb20..d11f7769ee5e 100644 --- a/media/java/android/media/Controller2Link.java +++ b/media/java/android/media/Controller2Link.java @@ -16,6 +16,7 @@ package android.media; +import android.os.Binder; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -102,6 +103,15 @@ public final class Controller2Link implements Parcelable { } } + /** Interface method for IMediaController2.notifyPlaybackActiveChanged */ + public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) { + try { + mIController.notifyPlaybackActiveChanged(seq, playbackActive); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + /** Interface method for IMediaController2.sendSessionCommand */ public void sendSessionCommand(int seq, Session2Command command, Bundle args, ResultReceiver resultReceiver) { @@ -135,6 +145,11 @@ public final class Controller2Link implements Parcelable { mController.onDisconnected(seq); } + /** Stub implementation for IMediaController2.notifyPlaybackActiveChanged */ + public void onPlaybackActiveChanged(int seq, boolean playbackActive) { + mController.onPlaybackActiveChanged(seq, playbackActive); + } + /** Stub implementation for IMediaController2.sendSessionCommand */ public void onSessionCommand(int seq, Session2Command command, Bundle args, ResultReceiver resultReceiver) { @@ -149,23 +164,53 @@ public final class Controller2Link implements Parcelable { private class Controller2Stub extends IMediaController2.Stub { @Override public void notifyConnected(int seq, Bundle connectionResult) { - Controller2Link.this.onConnected(seq, connectionResult); + final long token = Binder.clearCallingIdentity(); + try { + Controller2Link.this.onConnected(seq, connectionResult); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override public void notifyDisconnected(int seq) { - Controller2Link.this.onDisconnected(seq); + final long token = Binder.clearCallingIdentity(); + try { + Controller2Link.this.onDisconnected(seq); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) { + final long token = Binder.clearCallingIdentity(); + try { + Controller2Link.this.onPlaybackActiveChanged(seq, playbackActive); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override public void sendSessionCommand(int seq, Session2Command command, Bundle args, ResultReceiver resultReceiver) { - Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver); + final long token = Binder.clearCallingIdentity(); + try { + Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override public void cancelSessionCommand(int seq) { - Controller2Link.this.onCancelCommand(seq); + final long token = Binder.clearCallingIdentity(); + try { + Controller2Link.this.onCancelCommand(seq); + } finally { + Binder.restoreCallingIdentity(token); + } } } } diff --git a/media/java/android/media/IMediaController2.aidl b/media/java/android/media/IMediaController2.aidl index ca5394f504cb..42c6e70529ec 100644 --- a/media/java/android/media/IMediaController2.aidl +++ b/media/java/android/media/IMediaController2.aidl @@ -31,8 +31,9 @@ import android.media.Session2Command; oneway interface IMediaController2 { void notifyConnected(int seq, in Bundle connectionResult) = 0; void notifyDisconnected(int seq) = 1; + void notifyPlaybackActiveChanged(int seq, boolean playbackActive) = 2; void sendSessionCommand(int seq, in Session2Command command, in Bundle args, - in ResultReceiver resultReceiver) = 2; - void cancelSessionCommand(int seq) = 3; - // Next Id : 4 + in ResultReceiver resultReceiver) = 3; + void cancelSessionCommand(int seq) = 4; + // Next Id : 5 } diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index 8ec0e353ac73..9ac147b59ee5 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -150,7 +150,7 @@ public class ImageReader implements AutoCloseable { * consumer end-points. For example, if the application intends to send the images to * {@link android.media.MediaCodec} or {@link android.media.MediaRecorder} for hardware video * encoding, the format and usage flag combination needs to be - * {@link ImageFormat#PRIVATE PRIVATE} and {@link HardwareBuffer#USAGE0_VIDEO_ENCODE}. When an + * {@link ImageFormat#PRIVATE PRIVATE} and {@link HardwareBuffer#USAGE_VIDEO_ENCODE}. When an * {@link ImageReader} object is created with a valid size and such format/usage flag * combination, the application can send the {@link Image images} to an {@link ImageWriter} that * is created with the input {@link android.view.Surface} provided by the @@ -173,7 +173,7 @@ public class ImageReader implements AutoCloseable { * ImageReaders using other format such as {@link ImageFormat#YUV_420_888 YUV_420_888}. * </p> * <p> - * Note that not all format and usage flag combination is supported by the + * Note that not all format and usage flag combinations are supported by the * {@link ImageReader}. Below are the supported combinations by the {@link ImageReader} * (assuming the consumer end-points support the such image consumption, e.g., hardware video * encoding). @@ -186,13 +186,13 @@ public class ImageReader implements AutoCloseable { * <td>non-{@link android.graphics.ImageFormat#PRIVATE PRIVATE} formats defined by * {@link android.graphics.ImageFormat ImageFormat} or * {@link android.graphics.PixelFormat PixelFormat}</td> - * <td>{@link HardwareBuffer#USAGE0_CPU_READ} or - * {@link HardwareBuffer#USAGE0_CPU_READ_OFTEN}</td> + * <td>{@link HardwareBuffer#USAGE_CPU_READ_RARELY} or + * {@link HardwareBuffer#USAGE_CPU_READ_OFTEN}</td> * </tr> * <tr> * <td>{@link android.graphics.ImageFormat#PRIVATE}</td> - * <td>{@link HardwareBuffer#USAGE0_VIDEO_ENCODE} or - * {@link HardwareBuffer#USAGE0_GPU_SAMPLED_IMAGE}, or combined</td> + * <td>{@link HardwareBuffer#USAGE_VIDEO_ENCODE} or + * {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE}, or combined</td> * </tr> * </table> * Using other combinations may result in {@link IllegalArgumentException}. @@ -208,11 +208,10 @@ public class ImageReader implements AutoCloseable { * become available for access through {@link #acquireLatestImage()} or * {@link #acquireNextImage()}. Must be greater than 0. * @param usage The intended usage of the images produced by this ImageReader. It needs - * to be one of the Usage0 defined by {@link HardwareBuffer}, or an + * to be one of the Usage defined by {@link HardwareBuffer}, or an * {@link IllegalArgumentException} will be thrown. * @see Image * @see HardwareBuffer - * @hide */ public static ImageReader newInstance(int width, int height, int format, int maxImages, long usage) { diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index 4c0153f70192..dd09afc3ddb0 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -168,7 +168,6 @@ public class ImageWriter implements AutoCloseable { * {@link ImageFormat} or {@link PixelFormat}. * * @return a new ImageWriter instance. - * @hide */ public static ImageWriter newInstance(Surface surface, int maxImages, int format) { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { 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/MediaConstants.java b/media/java/android/media/MediaConstants.java index 5a5747ae4bab..65b6f55a068a 100644 --- a/media/java/android/media/MediaConstants.java +++ b/media/java/android/media/MediaConstants.java @@ -26,6 +26,7 @@ class MediaConstants { // Bundle key for Parcelable static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK"; static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS"; + static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE"; private MediaConstants() { } diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java index 165ea412333e..7c5335b71627 100644 --- a/media/java/android/media/MediaController2.java +++ b/media/java/android/media/MediaController2.java @@ -19,6 +19,7 @@ package android.media; import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS; import static android.media.MediaConstants.KEY_PACKAGE_NAME; import static android.media.MediaConstants.KEY_PID; +import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE; import static android.media.MediaConstants.KEY_SESSION2LINK; import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR; import static android.media.Session2Command.RESULT_INFO_SKIPPED; @@ -30,7 +31,6 @@ 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; @@ -82,6 +82,8 @@ public class MediaController2 implements AutoCloseable { private ArrayMap<ResultReceiver, Integer> mPendingCommands; //@GuardedBy("mLock") private ArraySet<Integer> mRequestedCommandSeqNumbers; + //@GuardedBy("mLock") + private boolean mPlaybackActive; /** * Create a {@link MediaController2} from the {@link Session2Token}. @@ -160,6 +162,18 @@ public class MediaController2 implements AutoCloseable { } /** + * Returns whether the session's playback is active. + * + * @return {@code true} if playback active. {@code false} otherwise. + * @see ControllerCallback#onPlaybackActiveChanged(MediaController2, boolean) + */ + public boolean isPlaybackActive() { + synchronized (mLock) { + return mPlaybackActive; + } + } + + /** * Sends a session command to the session * <p> * @param command the session command @@ -221,89 +235,82 @@ public class MediaController2 implements AutoCloseable { // Called by Controller2Link.onConnected void onConnected(int seq, Bundle connectionResult) { - final long token = Binder.clearCallingIdentity(); - try { - Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK); - Session2CommandGroup allowedCommands = - connectionResult.getParcelable(KEY_ALLOWED_COMMANDS); - if (DEBUG) { - Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder - + ", allowedCommands=" + allowedCommands); - } - if (sessionBinder == null || allowedCommands == null) { - // Connection rejected. - close(); - return; - } - synchronized (mLock) { - mSessionBinder = sessionBinder; - mAllowedCommands = allowedCommands; - // Implementation for the local binder is no-op, - // so can be used without worrying about deadlock. - sessionBinder.linkToDeath(mDeathRecipient, 0); - mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION, - mSessionToken.getPackageName(), sessionBinder); - } - mCallbackExecutor.execute(() -> { - mCallback.onConnected(MediaController2.this, allowedCommands); - }); - } finally { - Binder.restoreCallingIdentity(token); + Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK); + Session2CommandGroup allowedCommands = + connectionResult.getParcelable(KEY_ALLOWED_COMMANDS); + boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE); + if (DEBUG) { + Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder + + ", allowedCommands=" + allowedCommands); + } + if (sessionBinder == null || allowedCommands == null) { + // Connection rejected. + close(); + return; + } + synchronized (mLock) { + mSessionBinder = sessionBinder; + mAllowedCommands = allowedCommands; + mPlaybackActive = playbackActive; + + // Implementation for the local binder is no-op, + // so can be used without worrying about deadlock. + sessionBinder.linkToDeath(mDeathRecipient, 0); + mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION, + mSessionToken.getPackageName(), sessionBinder); } + mCallbackExecutor.execute(() -> { + mCallback.onConnected(MediaController2.this, allowedCommands); + }); } // Called by Controller2Link.onDisconnected void onDisconnected(int seq) { - final long token = Binder.clearCallingIdentity(); - try { - // close() will call mCallback.onDisconnected - close(); - } finally { - Binder.restoreCallingIdentity(token); + // close() will call mCallback.onDisconnected + close(); + } + + // Called by Controller2Link.onPlaybackActiveChanged + void onPlaybackActiveChanged(int seq, boolean playbackActive) { + synchronized (mLock) { + mPlaybackActive = playbackActive; } + mCallbackExecutor.execute(() -> { + mCallback.onPlaybackActiveChanged(MediaController2.this, playbackActive); + }); } // Called by Controller2Link.onSessionCommand void onSessionCommand(int seq, Session2Command command, Bundle args, @Nullable ResultReceiver resultReceiver) { - final long token = Binder.clearCallingIdentity(); - try { + synchronized (mLock) { + mRequestedCommandSeqNumbers.add(seq); + } + mCallbackExecutor.execute(() -> { + boolean isCanceled; synchronized (mLock) { - mRequestedCommandSeqNumbers.add(seq); + isCanceled = !mRequestedCommandSeqNumbers.remove(seq); } - mCallbackExecutor.execute(() -> { - boolean isCanceled; - synchronized (mLock) { - isCanceled = !mRequestedCommandSeqNumbers.remove(seq); - } - if (isCanceled) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); - return; - } - Session2Command.Result result = mCallback.onSessionCommand( - MediaController2.this, command, args); - if (resultReceiver != null) { - if (result == null) { - throw new RuntimeException("onSessionCommand shouldn't return null"); - } else { - resultReceiver.send(result.getResultCode(), result.getResultData()); - } + if (isCanceled) { + resultReceiver.send(RESULT_INFO_SKIPPED, null); + return; + } + Session2Command.Result result = mCallback.onSessionCommand( + MediaController2.this, 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 Controller2Link.onSessionCommand void onCancelCommand(int seq) { - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - mRequestedCommandSeqNumbers.remove(seq); - } - } finally { - Binder.restoreCallingIdentity(token); + synchronized (mLock) { + mRequestedCommandSeqNumbers.remove(seq); } } @@ -393,6 +400,17 @@ public class MediaController2 implements AutoCloseable { public void onDisconnected(@NonNull MediaController2 controller) {} /** + * Called when the playback of the session's playback activeness is changed. + * + * @param controller the controller for this event + * @param playbackActive {@code true} if the session's playback is active. + * {@code false} otherwise. + * @see MediaController2#isPlaybackActive() + */ + public void onPlaybackActiveChanged(@NonNull MediaController2 controller, + boolean playbackActive) {} + + /** * Called when the connected session sent a session command. * * @param controller the controller for this event 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/MediaSession2.java b/media/java/android/media/MediaSession2.java index dceef34596b7..3adac7295fff 100644 --- a/media/java/android/media/MediaSession2.java +++ b/media/java/android/media/MediaSession2.java @@ -19,6 +19,7 @@ package android.media; import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS; import static android.media.MediaConstants.KEY_PACKAGE_NAME; import static android.media.MediaConstants.KEY_PID; +import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE; import static android.media.MediaConstants.KEY_SESSION2LINK; import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR; import static android.media.Session2Command.RESULT_INFO_SKIPPED; @@ -87,6 +88,8 @@ public class MediaSession2 implements AutoCloseable { //@GuardedBy("mLock") private boolean mClosed; + //@GuardedBy("mLock") + private boolean mPlaybackActive; MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity, @NonNull Executor callbackExecutor, @NonNull SessionCallback callback) { @@ -215,6 +218,35 @@ public class MediaSession2 implements AutoCloseable { controller.cancelSessionCommand(token); } + /** + * Sets whether the playback is active (i.e. playing something) + * + * @param playbackActive {@code true} if the playback active, {@code false} otherwise. + **/ + public void setPlaybackActive(boolean playbackActive) { + synchronized (mLock) { + if (mPlaybackActive == playbackActive) { + return; + } + mPlaybackActive = playbackActive; + } + List<ControllerInfo> controllerInfos = getConnectedControllers(); + for (ControllerInfo controller : controllerInfos) { + controller.notifyPlaybackActiveChanged(playbackActive); + } + } + + /** + * Returns whehther the playback is active (i.e. playing something) + * + * @return {@code true} if the playback active, {@code false} otherwise. + */ + public boolean isPlaybackActive() { + synchronized (mLock) { + return mPlaybackActive; + } + } + boolean isClosed() { synchronized (mLock) { return mClosed; @@ -239,7 +271,7 @@ public class MediaSession2 implements AutoCloseable { final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo, mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller); mCallbackExecutor.execute(() -> { - boolean accept = false; + boolean connected = false; try { if (isClosed()) { return; @@ -249,8 +281,7 @@ 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. - accept = controllerInfo.mAllowedCommands != null || controllerInfo.isTrusted(); - if (!accept) { + if (controllerInfo.mAllowedCommands == null && !controllerInfo.isTrusted()) { return; } if (controllerInfo.mAllowedCommands == null) { @@ -276,6 +307,7 @@ public class MediaSession2 implements AutoCloseable { connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub); connectionResult.putParcelable(KEY_ALLOWED_COMMANDS, controllerInfo.mAllowedCommands); + connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive()); // Double check if session is still there, because close() can be called in // another thread. @@ -283,13 +315,18 @@ public class MediaSession2 implements AutoCloseable { return; } controllerInfo.notifyConnected(connectionResult); + connected = true; } finally { - if (!accept) { + if (!connected) { if (DEBUG) { - Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo); + Log.d(TAG, "Rejecting connection or notifying that session is closed" + + ", controllerInfo=" + controllerInfo); + } + synchronized (mLock) { + mConnectedControllers.remove(controller); } + controllerInfo.notifyDisconnected(); } - controllerInfo.notifyDisconnected(); } }); } @@ -587,6 +624,16 @@ public class MediaSession2 implements AutoCloseable { } } + void notifyPlaybackActiveChanged(boolean playbackActive) { + if (mControllerBinder == null) return; + + try { + mControllerBinder.notifyPlaybackActiveChanged(getNextSeqNumber(), playbackActive); + } catch (RuntimeException e) { + // Controller may be died prematurely. + } + } + void sendSessionCommand(Session2Command command, Bundle args, ResultReceiver resultReceiver) { if (mControllerBinder == null) return; 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/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_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/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index bce559396ddd..5105ff46703d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -84,6 +84,8 @@ import com.android.providers.settings.SettingsState.Setting; import com.android.server.LocalServices; import com.android.server.SystemConfig; +import com.google.android.collect.Sets; + import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -957,12 +959,12 @@ public class SettingsProvider extends ContentProvider { UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class); userManager.addUserRestrictionsListener((int userId, Bundle newRestrictions, Bundle prevRestrictions) -> { + Set<String> changedRestrictions = getRestrictionDiff(prevRestrictions, newRestrictions); // We are changing the settings affected by restrictions to their current // value with a forced update to ensure that all cross profile dependencies // are taken into account. Also make sure the settings update to.. the same // value passes the security checks, so clear binder calling id. - if (newRestrictions.getBoolean(UserManager.DISALLOW_SHARE_LOCATION) - != prevRestrictions.getBoolean(UserManager.DISALLOW_SHARE_LOCATION)) { + if (changedRestrictions.contains(UserManager.DISALLOW_SHARE_LOCATION)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -976,11 +978,8 @@ public class SettingsProvider extends ContentProvider { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) - != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) || - newRestrictions.getBoolean( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY) - != prevRestrictions.getBoolean( + if (changedRestrictions.contains(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) + || changedRestrictions.contains( UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)) { final long identity = Binder.clearCallingIdentity(); try { @@ -994,8 +993,7 @@ public class SettingsProvider extends ContentProvider { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.getBoolean(UserManager.DISALLOW_DEBUGGING_FEATURES) - != prevRestrictions.getBoolean(UserManager.DISALLOW_DEBUGGING_FEATURES)) { + if (changedRestrictions.contains(UserManager.DISALLOW_DEBUGGING_FEATURES)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -1008,8 +1006,7 @@ public class SettingsProvider extends ContentProvider { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.getBoolean(UserManager.ENSURE_VERIFY_APPS) - != prevRestrictions.getBoolean(UserManager.ENSURE_VERIFY_APPS)) { + if (changedRestrictions.contains(UserManager.ENSURE_VERIFY_APPS)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -1028,8 +1025,7 @@ public class SettingsProvider extends ContentProvider { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) - != prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { + if (changedRestrictions.contains(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -1046,13 +1042,27 @@ public class SettingsProvider extends ContentProvider { }); } + private static Set<String> getRestrictionDiff(Bundle prevRestrictions, Bundle newRestrictions) { + Set<String> restrictionNames = Sets.newArraySet(); + restrictionNames.addAll(prevRestrictions.keySet()); + restrictionNames.addAll(newRestrictions.keySet()); + Set<String> diff = Sets.newArraySet(); + for (String restrictionName : restrictionNames) { + if (prevRestrictions.getBoolean(restrictionName) != newRestrictions.getBoolean( + restrictionName)) { + diff.add(restrictionName); + } + } + return diff; + } + private Setting getConfigSetting(String name) { if (DEBUG) { Slog.v(LOG_TAG, "getConfigSetting(" + name + ")"); } // 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 +1098,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/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml index 0d72657bb2df..6f7f39810608 100644 --- a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml @@ -19,15 +19,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - <TextClock + <include android:id="@+id/digital_clock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:letterSpacing="0.03" - 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" /> <com.android.keyguard.clock.ImageClock android:id="@+id/analog_clock" diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml index cf4a573834dd..e88e2c94e74f 100644 --- a/packages/SystemUI/res-keyguard/layout/digital_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml @@ -20,16 +20,9 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_alignParentTop="true"> - <TextClock + <include android:id="@+id/lock_screen_clock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:letterSpacing="0.03" - 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> 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 index 9033fce881c4..64b676f55fd6 100644 --- a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml @@ -19,15 +19,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - <TextClock + <include android:id="@+id/digital_clock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:letterSpacing="0.03" - 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" /> <com.android.keyguard.clock.StretchAnalogClock android:id="@+id/analog_clock" 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 index 7a849ebd481d..74ee7ffad3f6 100644 --- a/packages/SystemUI/res-keyguard/values/colors.xml +++ b/packages/SystemUI/res-keyguard/values/colors.xml @@ -19,4 +19,6 @@ <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/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 9245c300acf5..e2ba23ec898c 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -47,8 +47,6 @@ <dimen name="bottom_text_spacing_digital">0dp</dimen> <!-- Slice subtitle --> <dimen name="widget_label_font_size">16dp</dimen> - <!-- Slice offset when pulsing --> - <dimen name="widget_pulsing_bottom_padding">48dp</dimen> <!-- Clock without header --> <dimen name="widget_big_font_size">64dp</dimen> <!-- Clock with header --> 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/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 5a00b4526c32..ab0bbe10c37c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -66,9 +66,7 @@ <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item> <dimen name="group_overflow_number_size">@*android:dimen/notification_text_size</dimen> - <dimen name="group_overflow_number_size_dark">16sp</dimen> <dimen name="group_overflow_number_padding">@*android:dimen/notification_content_margin_end</dimen> - <dimen name="group_overflow_number_extra_padding_dark">@*android:dimen/notification_extra_margin_ambient</dimen> <!-- max height of a notification such that the content can still fade out when closing --> <dimen name="max_notification_fadeout_height">100dp</dimen> @@ -94,9 +92,6 @@ <!-- Height of a large notification in the status bar --> <dimen name="notification_max_height">294dp</dimen> - <!-- Height of an ambient notification on ambient display --> - <dimen name="notification_ambient_height">400dp</dimen> - <!-- Height of a heads up notification in the status bar for legacy custom views --> <dimen name="notification_max_heads_up_height_legacy">128dp</dimen> 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/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 22a0b3642a35..fa675b75d85d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -64,48 +64,16 @@ <item name="hybridNotificationTextStyle">@style/hybrid_notification_text</item> </style> - <style name="HybridNotification.Ambient"> - <item name="hybridNotificationStyle">@style/hybrid_notification_ambient</item> - <item name="hybridNotificationTitleStyle">@style/hybrid_notification_title_ambient</item> - <item name="hybridNotificationTextStyle">@style/hybrid_notification_text_ambient</item> - </style> - - <style name="hybrid_notification_ambient"> - <item name="android:paddingStart">@*android:dimen/notification_extra_margin_ambient</item> - <item name="android:paddingEnd">@*android:dimen/notification_extra_margin_ambient</item> - <item name="android:orientation">vertical</item> - </style> - <style name="hybrid_notification"> <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item> <item name="android:paddingEnd">12dp</item> </style> - <style name="hybrid_notification_title_ambient"> - <item name="android:layout_marginTop">@*android:dimen/notification_header_margin_top_ambient</item> - <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item> - <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item> - <item name="android:textAppearance">@*android:style/Notification.Header.Ambient</item> - <item name="android:layout_gravity">top|center_horizontal</item> - <item name="android:textSize">@*android:dimen/notification_ambient_title_text_size</item> - <item name="android:textColor">#ffffffff</item> - </style> - <style name="hybrid_notification_title"> <item name="android:paddingEnd">4dp</item> <item name="android:textAppearance">@*android:style/TextAppearance.Material.Notification.Title</item> </style> - <style name="hybrid_notification_text_ambient"> - <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item> - <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item> - <item name="android:textSize">@*android:dimen/notification_ambient_text_size</item> - <item name="android:textColor">#eeffffff</item> - <item name="android:gravity">top|center_horizontal</item> - <item name="android:ellipsize">end</item> - <item name="android:maxLines">3</item> - </style> - <style name="hybrid_notification_text" parent="@*android:style/Widget.Material.Notification.Text"> <item name="android:paddingEnd">4dp</item> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index a8094d20d5a2..1aff3949a74b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -20,6 +20,7 @@ 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; @@ -153,6 +154,12 @@ public class KeyguardClockSwitch extends RelativeLayout { 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), @@ -248,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); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index 669e6fff525b..bac7844c024b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -90,7 +90,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe */ private Runnable mContentChangeListener; private Slice mSlice; - private boolean mPulsing; + private boolean mHasHeader; public KeyguardSliceView(Context context) { this(context, null, 0); @@ -150,10 +150,18 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe Dependency.get(ConfigurationController.class).removeCallback(this); } + /** + * Returns whether the current visible slice has a title/header. + */ + public boolean hasHeader() { + return mHasHeader; + } + private void showSlice() { Trace.beginSection("KeyguardSliceView#showSlice"); - if (mPulsing || mSlice == null) { + if (mSlice == null) { mRow.setVisibility(GONE); + mHasHeader = false; if (mContentChangeListener != null) { mContentChangeListener.run(); } @@ -162,7 +170,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe ListContent lc = new ListContent(getContext(), mSlice); SliceContent headerContent = lc.getHeader(); - boolean hasHeader = headerContent != null + mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); List<SliceContent> subItems = new ArrayList<>(); for (int i = 0; i < lc.getRowItems().size(); i++) { @@ -177,7 +185,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mClickActions.clear(); final int subItemsCount = subItems.size(); final int blendedColor = getTextColor(); - final int startIndex = hasHeader ? 1 : 0; // First item is header; skip it + final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE); for (int i = startIndex; i < subItemsCount; i++) { RowContent rc = (RowContent) subItems.get(i); @@ -189,7 +197,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe button = new KeyguardSliceButton(mContext); button.setTextColor(blendedColor); button.setTag(itemTag); - final int viewIndex = i - (hasHeader ? 1 : 0); + final int viewIndex = i - (mHasHeader ? 1 : 0); mRow.addView(button, viewIndex); } @@ -234,18 +242,6 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe Trace.endSection(); } - public void setPulsing(boolean pulsing, boolean animate) { - mPulsing = pulsing; - LayoutTransition transition = getLayoutTransition(); - if (!animate) { - setLayoutTransition(null); - } - showSlice(); - if (!animate) { - setLayoutTransition(transition); - } - } - public void setDarkAmount(float darkAmount) { mDarkAmount = darkAmount; mRow.setDarkAmount(darkAmount); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index f0cdc890306f..bb549ada6830 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -72,7 +72,6 @@ public class KeyguardStatusView extends GridLayout implements private ArraySet<View> mVisibleInDoze; private boolean mPulsing; - private boolean mWasPulsing; private float mDarkAmount = 0; private int mTextColor; private int mLastLayoutHeight; @@ -202,10 +201,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); } /** @@ -214,7 +209,7 @@ public class KeyguardStatusView extends GridLayout implements private void onSliceContentChanged() { LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mClockView.getLayoutParams(); - layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0; + layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0; mClockView.setLayoutParams(layoutParams); } @@ -224,16 +219,16 @@ public class KeyguardStatusView extends GridLayout implements @Override public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - int heightOffset = mPulsing || mWasPulsing ? 0 : getHeight() - mLastLayoutHeight; + boolean smallClock = mKeyguardSlice.hasHeader(); + int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight; long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION; - long delay = mPulsing || mWasPulsing ? 0 : duration / 4; - mWasPulsing = false; + long delay = smallClock ? 0 : duration / 4; boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null && mKeyguardSlice.getLayoutTransition().isRunning(); if (view == mClockView) { - float clockScale = mPulsing ? mSmallClockScale : 1; - Paint.Style style = mPulsing ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL; + float clockScale = smallClock ? mSmallClockScale : 1; + Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL; mClockView.animate().cancel(); if (shouldAnimate) { mClockView.setY(oldTop + heightOffset); @@ -438,15 +433,11 @@ public class KeyguardStatusView extends GridLayout implements } } - public void setPulsing(boolean pulsing, boolean animate) { + public void setPulsing(boolean pulsing) { if (mPulsing == pulsing) { return; } - if (mPulsing) { - mWasPulsing = true; - } mPulsing = pulsing; - mKeyguardSlice.setPulsing(pulsing, animate); updateDozeVisibleViews(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java index 5aa566848732..3591dc82c8ec 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -36,6 +36,7 @@ public class ClockLayout extends FrameLayout { */ private View mDigitalClock; private View mAnalogClock; + private View mTypeClock; /** * Pixel shifting amplitidues used to prevent screen burn-in. @@ -60,6 +61,7 @@ public class ClockLayout extends FrameLayout { super.onFinishInflate(); mDigitalClock = findViewById(R.id.digital_clock); mAnalogClock = findViewById(R.id.analog_clock); + mTypeClock = findViewById(R.id.type_clock); // Get pixel shifting X, Y amplitudes from resources. Resources resources = getResources(); @@ -89,5 +91,11 @@ public class ClockLayout extends FrameLayout { mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) + offsetY); } + + // Put the typographic clock part way down the screen. + if (mTypeClock != null) { + mTypeClock.setX(offsetX); + mTypeClock.setY(0.2f * getHeight() + offsetY); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java index 91cec86392d6..8734754541a6 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java @@ -80,6 +80,7 @@ public class StretchAnalogClock extends View { */ public void setMinuteHandColor(int color) { mMinutePaint.setColor(color); + invalidate(); } private void init() { 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/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/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index bd9ca1aa32f8..9ef9c94d7cdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -65,12 +65,12 @@ public class NotificationShelf extends ActivatableNotificationView implements private static final String TAG = "NotificationShelf"; private static final long SHELF_IN_TRANSLATION_DURATION = 200; - private boolean mDark; private NotificationIconContainer mShelfIcons; private int[] mTmp = new int[2]; private boolean mHideBackground; private int mIconAppearTopPadding; private int mShelfAppearTranslation; + private float mDarkShelfPadding; private int mStatusBarHeight; private int mStatusBarPaddingStart; private AmbientState mAmbientState; @@ -140,6 +140,7 @@ public class NotificationShelf extends ActivatableNotificationView implements mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start); mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height); mShelfAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation); + mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding); ViewGroup.LayoutParams layoutParams = getLayoutParams(); layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height); @@ -165,11 +166,11 @@ public class NotificationShelf extends ActivatableNotificationView implements @Override public void setDark(boolean dark, boolean fade, long delay) { - super.setDark(dark, fade, delay); if (mDark == dark) return; - mDark = dark; + super.setDark(dark, fade, delay); mShelfIcons.setDark(dark, fade, delay); updateInteractiveness(); + updateOutline(); } /** @@ -218,10 +219,9 @@ public class NotificationShelf extends ActivatableNotificationView implements float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height, getFullyClosedTranslation()); - float darkTranslation = mAmbientState.getDarkTopPadding(); float yRatio = mAmbientState.hasPulsingNotifications() ? 0 : mAmbientState.getDarkAmount(); - viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio); + viewState.yTranslation = awakenTranslation + mDarkShelfPadding * yRatio; viewState.zTranslation = ambientState.getBaseZHeight(); // For the small display size, it's not enough to make the icon not covered by // the top cutout so the denominator add the height of cutout. @@ -763,18 +763,14 @@ public class NotificationShelf extends ActivatableNotificationView implements } } - public boolean hidesBackground() { - return mHideBackground; - } - @Override protected boolean needsOutline() { - return !mHideBackground && super.needsOutline(); + return !mHideBackground && !mDark && super.needsOutline(); } @Override protected boolean shouldHideBackground() { - return super.shouldHideBackground() || mHideBackground; + return super.shouldHideBackground() || mHideBackground || mDark; } @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/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 8b0a6828205d..c34d567da0be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -105,7 +105,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private final DoubleTapHelper mDoubleTapHelper; private boolean mDimmed; - private boolean mDark; + protected boolean mDark; protected int mBgTint = NO_COLOR; private float mBgAlpha = 1f; @@ -140,7 +140,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private FalsingManager mFalsingManager; private float mNormalBackgroundVisibilityAmount; - private ValueAnimator mFadeInFromDarkAnimator; private float mDimmedBackgroundFadeInAmount = -1; private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater = new ValueAnimator.AnimatorUpdateListener() { @@ -150,22 +149,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha(); } }; - private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mFadeInFromDarkAnimator = null; - mDimmedBackgroundFadeInAmount = -1; - updateBackground(); - } - }; - private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener - = new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - updateOutlineAlpha(); - } - }; private FakeShadowView mFakeShadow; private int mCurrentBackgroundTint; private int mTargetTint; @@ -465,22 +448,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mDark = dark; updateBackground(); updateBackgroundTint(false); - if (!dark && fade && !shouldHideBackground()) { - fadeInFromDark(delay); - } - updateOutlineAlpha(); } private void updateOutlineAlpha() { - if (mDark) { - setOutlineAlpha(0f); - return; - } float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED; alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount); - if (mFadeInFromDarkAnimator != null) { - alpha *= mFadeInFromDarkAnimator.getAnimatedFraction(); - } setOutlineAlpha(alpha); } @@ -638,36 +610,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } /** - * Fades in the background when exiting dark mode. - */ - private void fadeInFromDark(long delay) { - final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal; - background.setAlpha(0f); - mBackgroundVisibilityUpdater.onAnimationUpdate(null); - background.animate() - .alpha(1f) - .setDuration(DARK_ANIMATION_LENGTH) - .setStartDelay(delay) - .setInterpolator(Interpolators.ALPHA_IN) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - // Jump state if we are cancelled - background.setAlpha(1f); - } - }) - .setUpdateListener(mBackgroundVisibilityUpdater) - .start(); - mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f); - mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH); - mFadeInFromDarkAnimator.setStartDelay(delay); - mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - mFadeInFromDarkAnimator.addListener(mFadeInEndListener); - mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener); - mFadeInFromDarkAnimator.start(); - } - - /** * Fades the background when the dimmed state changes. */ private void fadeDimmedBackground() { @@ -708,9 +650,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView public void onAnimationEnd(Animator animation) { updateBackground(); mBackgroundAnimator = null; - if (mFadeInFromDarkAnimator == null) { - mDimmedBackgroundFadeInAmount = -1; - } + mDimmedBackgroundFadeInAmount = -1; } }); mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater); @@ -736,7 +676,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE); } else if (mDimmed) { // When groups are animating to the expanded state from the lockscreen, show the - // normal background instead of the dimmed background + // normal background instead of the dimmed background. final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup(); mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE); mBackgroundNormal.setVisibility((mActivated || dontShowDimmed) @@ -760,7 +700,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } protected boolean shouldHideBackground() { - return mDark; + return false; } private void cancelFadeAnimations() { @@ -1023,14 +963,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView /** * @param withTint should a possible tint be factored in? - * @param withOverRide should the value be interpolated with {@link #mOverrideTint} + * @param withOverride should the value be interpolated with {@link #mOverrideTint} * @return the calculated background color */ - private int calculateBgColor(boolean withTint, boolean withOverRide) { - if (withTint && mDark) { - return getContext().getColor(R.color.notification_material_background_dark_color); - } - if (withOverRide && mOverrideTint != NO_COLOR) { + private int calculateBgColor(boolean withTint, boolean withOverride) { + if (withOverride && mOverrideTint != NO_COLOR) { int defaultTint = calculateBgColor(withTint, false); return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index df0189fc4c4a..296c061459bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -132,7 +132,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private LayoutListener mLayoutListener; - private boolean mDark; private boolean mLowPriorityStateUpdated; private final NotificationInflater mNotificationInflater; private int mIconTransformContentShift; @@ -146,7 +145,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationMinHeight; private int mNotificationMinHeightLarge; private int mNotificationMaxHeight; - private int mNotificationAmbientHeight; private int mIncreasedPaddingBetweenElements; private int mNotificationLaunchHeight; private boolean mMustStayOnScreen; @@ -677,8 +675,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (headsUpWrapper != null) { headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight()); } - layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight, - mNotificationAmbientHeight); + layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight, headsUpHeight); } public StatusBarNotification getStatusBarNotification() { @@ -1647,8 +1644,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView R.dimen.notification_min_height_increased); mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_height); - mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext, - R.dimen.notification_ambient_height); mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_heads_up_height_legacy); mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext, @@ -2008,8 +2003,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void setDark(boolean dark, boolean fade, long delay) { + if (mDark == dark) { + return; + } super.setDark(dark, fade, delay); - mDark = dark; if (!mIsAmbientPulsing) { // Only fade the showing view of the pulsing notification. fade = false; @@ -2018,9 +2015,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (showing != null) { showing.setDark(dark, fade, delay); } - if (mIsSummaryWithChildren) { - mChildrenContainer.setDark(dark, fade, delay); - } updateShelfIconColor(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java index 33badafd5488..90ff4a728915 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java @@ -39,14 +39,10 @@ public class HybridGroupManager { private final NotificationDozeHelper mDozer; private final ViewGroup mParent; - private float mOverflowNumberSizeDark; - private int mOverflowNumberPaddingDark; private float mOverflowNumberSize; private int mOverflowNumberPadding; private int mOverflowNumberColor; - private int mOverflowNumberColorDark; - private float mDarkAmount = 0f; public HybridGroupManager(Context ctx, ViewGroup parent) { mContext = ctx; @@ -59,12 +55,8 @@ public class HybridGroupManager { Resources res = mContext.getResources(); mOverflowNumberSize = res.getDimensionPixelSize( R.dimen.group_overflow_number_size); - mOverflowNumberSizeDark = res.getDimensionPixelSize( - R.dimen.group_overflow_number_size_dark); mOverflowNumberPadding = res.getDimensionPixelSize( R.dimen.group_overflow_number_padding); - mOverflowNumberPaddingDark = mOverflowNumberPadding + res.getDimensionPixelSize( - R.dimen.group_overflow_number_extra_padding_dark); } private HybridNotificationView inflateHybridViewWithStyle(int style) { @@ -86,13 +78,11 @@ public class HybridGroupManager { } private void updateOverFlowNumberColor(TextView numberView) { - numberView.setTextColor(NotificationUtils.interpolateColors( - mOverflowNumberColor, mOverflowNumberColorDark, mDarkAmount)); + numberView.setTextColor(mOverflowNumberColor); } - public void setOverflowNumberColor(TextView numberView, int colorRegular, int colorDark) { + public void setOverflowNumberColor(TextView numberView, int colorRegular) { mOverflowNumberColor = colorRegular; - mOverflowNumberColorDark = colorDark; if (numberView != null) { updateOverFlowNumberColor(numberView); } @@ -107,7 +97,7 @@ public class HybridGroupManager { public HybridNotificationView bindAmbientFromNotification(HybridNotificationView reusableView, Notification notification) { return bindFromNotificationWithStyle(reusableView, notification, - R.style.HybridNotification_Ambient); + R.style.HybridNotification); } private HybridNotificationView bindFromNotificationWithStyle( @@ -150,6 +140,11 @@ public class HybridGroupManager { R.plurals.notification_group_overflow_description, number), number); reusableView.setContentDescription(contentDescription); + reusableView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mOverflowNumberSize); + reusableView.setPaddingRelative(reusableView.getPaddingStart(), + reusableView.getPaddingTop(), mOverflowNumberPadding, + reusableView.getPaddingBottom()); + updateOverFlowNumberColor(reusableView); return reusableView; } @@ -163,16 +158,4 @@ public class HybridGroupManager { } return titleView; } - - public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) { - mDozer.setIntensityDark((f)->{ - mDarkAmount = f; - updateOverFlowNumberColor(view); - }, dark, fade, delay, view); - view.setTextSize(TypedValue.COMPLEX_UNIT_PX, - dark ? mOverflowNumberSizeDark : mOverflowNumberSize); - int paddingEnd = dark ? mOverflowNumberPaddingDark : mOverflowNumberPadding; - view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd, - view.getPaddingBottom()); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index cbec37effe18..1b5013dedb0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -75,7 +75,6 @@ public class AmbientState { private int mIntrinsicPadding; private int mExpandAnimationTopChange; private ExpandableNotificationRow mExpandingNotification; - private int mDarkTopPadding; private float mDarkAmount; private boolean mAppearing; @@ -351,7 +350,8 @@ public class AmbientState { } public boolean hasPulsingNotifications() { - return mPulsing; + return mPulsing && mAmbientPulseManager != null + && mAmbientPulseManager.hasNotifications(); } public void setPulsing(boolean hasPulsing) { @@ -458,14 +458,6 @@ public class AmbientState { return mDarkAmount != 0; } - public void setDarkTopPadding(int darkTopPadding) { - mDarkTopPadding = darkTopPadding; - } - - public int getDarkTopPadding() { - return mDarkTopPadding; - } - public void setAppearing(boolean appearing) { mAppearing = appearing; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 51180361806b..8ffada43c53f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -316,7 +316,7 @@ public class NotificationChildrenContainer extends ViewGroup { StatusBarNotification notification = mContainingNotification.getStatusBarNotification(); final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(), notification.getNotification()); - RemoteViews header = builder.makeNotificationHeader(false /* ambient */); + RemoteViews header = builder.makeNotificationHeader(); if (mNotificationHeader == null) { mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this); final View expandButton = mNotificationHeader.findViewById( @@ -344,7 +344,7 @@ public class NotificationChildrenContainer extends ViewGroup { builder = Notification.Builder.recoverBuilder(getContext(), notification.getNotification()); } - header = builder.makeNotificationHeader(true /* ambient */); + header = builder.makeNotificationHeader(); if (mNotificationHeaderAmbient == null) { mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this); mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(), @@ -1171,12 +1171,6 @@ public class NotificationChildrenContainer extends ViewGroup { return mIsLowPriority && !mContainingNotification.isExpanded(); } - public void setDark(boolean dark, boolean fade, long delay) { - if (mOverflowNumber != null) { - mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay); - } - } - public void reInflateViews(OnClickListener listener, StatusBarNotification notification) { if (mNotificationHeader != null) { removeView(mNotificationHeader); @@ -1227,8 +1221,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void onNotificationUpdated() { mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, - mContainingNotification.getNotificationColor(), - mContainingNotification.getNotificationColorAmbient()); + mContainingNotification.getNotificationColor()); } public int getPositionInLinearLayout(View childInGroup) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 941860177d1b..c5ab9f6049c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -26,26 +27,34 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.util.HashSet; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * A class that manages the roundness for notification views */ -class NotificationRoundnessManager implements OnHeadsUpChangedListener { - +@Singleton +class NotificationRoundnessManager implements OnHeadsUpChangedListener, + AmbientPulseManager.OnAmbientChangedListener { + + private final ActivatableNotificationView[] mFirstInSectionViews; + private final ActivatableNotificationView[] mLastInSectionViews; + private final ActivatableNotificationView[] mTmpFirstInSectionViews; + private final ActivatableNotificationView[] mTmpLastInSectionViews; private boolean mExpanded; - private ActivatableNotificationView[] mFirstInSectionViews; - private ActivatableNotificationView[] mLastInSectionViews; - private ActivatableNotificationView[] mTmpFirstInSectionViews; - private ActivatableNotificationView[] mTmpLastInSectionViews; private HashSet<ExpandableView> mAnimatedChildren; private Runnable mRoundingChangedCallback; private ExpandableNotificationRow mTrackedHeadsUp; + private ActivatableNotificationView mTrackedAmbient; private float mAppearFraction; - NotificationRoundnessManager() { + @Inject + NotificationRoundnessManager(AmbientPulseManager ambientPulseManager) { mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; + ambientPulseManager.addListener(this); } @Override @@ -63,6 +72,17 @@ class NotificationRoundnessManager implements OnHeadsUpChangedListener { updateView(row, false /* animate */); } + @Override + public void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing) { + ActivatableNotificationView row = entry.getRow(); + if (isPulsing) { + mTrackedAmbient = row; + } else if (mTrackedAmbient == row) { + mTrackedAmbient = null; + } + updateView(row, false /* animate */); + } + private void updateView(ActivatableNotificationView view, boolean animate) { boolean changed = updateViewWithoutCallback(view, animate); if (changed) { @@ -125,6 +145,9 @@ class NotificationRoundnessManager implements OnHeadsUpChangedListener { // rounded. return 1.0f; } + if (view == mTrackedAmbient) { + return 1.0f; + } return 0.0f; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 2d1f989fb1c9..d0665670bf8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -201,12 +201,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mPaddingBetweenElements; private int mIncreasedPaddingBetweenElements; private int mMaxTopPadding; - private int mRegularTopPadding; - private int mDarkTopPadding; - // Current padding, will be either mRegularTopPadding or mDarkTopPadding private int mTopPadding; - // Distance between AOD separator and shelf - private int mDarkShelfPadding; private int mBottomMargin; private int mBottomInset = 0; private float mQsExpansionFraction; @@ -318,7 +313,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations = new HashSet<>(); private HeadsUpManagerPhone mHeadsUpManager; - private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager(); + private final NotificationRoundnessManager mRoundnessManager; private boolean mTrackingHeadsUp; private ScrimController mScrimController; private boolean mForceNoOverlappingRendering; @@ -441,7 +436,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd Dependency.get(NotificationEntryManager.class); private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + @VisibleForTesting + protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final NotificationRemoteInputManager mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); @@ -468,7 +464,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) { + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, + NotificationRoundnessManager notificationRoundnessManager) { super(context, attrs, 0, 0); Resources res = getResources(); @@ -479,6 +476,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } mAmbientState = new AmbientState(context); + mRoundnessManager = notificationRoundnessManager; mBgColor = context.getColor(R.color.notification_shade_background_color); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); @@ -495,7 +493,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd res.getBoolean(R.bool.config_drawNotificationBackground); mFadeNotificationsOnDismiss = res.getBoolean(R.bool.config_fadeNotificationsOnDismiss); - mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding); mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated); mRoundnessManager.setOnRoundingChangedCallback(this::invalidate); addOnExpandedHeightListener(mRoundnessManager::setExpanded); @@ -684,7 +681,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd int lockScreenTop = mSections[0].getCurrentBounds().top; int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom; int darkLeft = getWidth() / 2; - int darkTop = mRegularTopPadding; + int darkTop = mTopPadding; float yProgress = 1 - mInterpolatedDarkAmount; float xProgress = mDarkXInterpolator.getInterpolation( @@ -952,8 +949,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) private void updateAlgorithmHeightAndPadding() { - mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, - mInterpolatedDarkAmount); mAmbientState.setLayoutHeight(getLayoutHeight()); updateAlgorithmLayoutMinHeight(); mAmbientState.setTopPadding(mTopPadding); @@ -1091,10 +1086,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void setTopPadding(int topPadding, boolean animate) { - if (mRegularTopPadding != topPadding) { - mRegularTopPadding = topPadding; - mDarkTopPadding = topPadding + mDarkShelfPadding; - mAmbientState.setDarkTopPadding(mDarkTopPadding); + if (mTopPadding != topPadding) { + mTopPadding = topPadding; updateAlgorithmHeightAndPadding(); updateContentHeight(); if (animate && mAnimationsEnabled && mIsExpanded) { @@ -2059,10 +2052,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } mIntrinsicContentHeight = height; - // We don't want to use the toppadding since that might be interpolated and we want - // to take the final value of the animation. - int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding; - mContentHeight = height + topPadding + mBottomMargin; + mContentHeight = height + mTopPadding + mBottomMargin; updateScrollability(); clampScrollPosition(); mAmbientState.setLayoutMaxHeight(mContentHeight); @@ -2226,13 +2216,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd int top = 0; if (section != null) { ActivatableNotificationView firstView = section.getFirstVisibleChild(); - // Round Y up to avoid seeing the background during animation - int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView)); - if (alreadyAnimating || section.isTargetTop(finalTranslationY)) { - // we're ending up at the same location as we are now, let's just skip the animation - top = finalTranslationY; - } else { - top = (int) Math.ceil(firstView.getTranslationY()); + if (firstView != null) { + // Round Y up to avoid seeing the background during animation + int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView)); + if (alreadyAnimating || section.isTargetTop(finalTranslationY)) { + // we're ending up at the same location as we are now, let's just skip the + // animation + top = finalTranslationY; + } else { + top = (int) Math.ceil(firstView.getTranslationY()); + } } } return top; @@ -5218,9 +5211,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // another "changeViewPosition" call is ever added. changeViewPosition(mShelf, getChildCount() - offsetFromEnd); - - // Scrim opacity varies based on notification count - mScrimController.setNotificationCount(getNotGoneChildCount()); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -5638,8 +5628,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } }; + @VisibleForTesting @ShadeViewRefactor(RefactorComponent.INPUT) - private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { + protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { @Override public void onMenuClicked(View view, int x, int y, MenuItem item) { if (mLongPressListener == null) { @@ -5647,8 +5638,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (view instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; - MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR, - row.getStatusBarNotification().getPackageName()); + mMetricsLogger.write(row.getStatusBarNotification().getLogMaker() + .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) + .setType(MetricsEvent.TYPE_ACTION) + ); } mLongPressListener.onLongPress(view, x, y, item); } @@ -5671,8 +5664,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public void onMenuShown(View row) { if (row instanceof ExpandableNotificationRow) { ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; - MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR, - notificationRow.getStatusBarNotification().getPackageName()); + mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker() + .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) + .setType(MetricsEvent.TYPE_ACTION)); mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); } mSwipeHelper.onMenuShown(row); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 19fce485b0c7..b4c205ab980c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -426,6 +426,9 @@ public class StackStateAnimator { mTmpState.yTranslation += mPulsingAppearingTranslation; mTmpState.alpha = 0; mTmpState.applyToView(changingView); + + mTmpState.copyFrom(mShelf.getViewState()); + mTmpState.applyToView(mShelf); } } else if (event.animationType == NotificationStackScrollLayout .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 6f2b63d2e64d..10497734d0b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -158,8 +158,7 @@ public class DozeParameters implements TunerService.Tunable { * @return duration in millis. */ public long getWallpaperAodDuration() { - if (mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT) - || shouldControlScreenOff()) { + if (shouldControlScreenOff()) { return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY; } return mAlwaysOnPolicy.wallpaperVisibilityDuration; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 577e8d614acb..280dda0cd1dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -84,14 +84,6 @@ public class DozeScrimController implements StateListener { public void onCancelled() { pulseFinished(); } - - /** - * Whether to fade out wallpaper. - */ - @Override - public boolean isFadeOutWallpaper() { - return mPulseReason == DozeLog.PULSE_REASON_DOCKING; - } }; public DozeScrimController(DozeParameters dozeParameters) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index a81b7e572a79..c68fdd4ec4a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -91,11 +91,6 @@ public class KeyguardClockPositionAlgorithm { private int mBurnInPreventionOffsetY; /** - * Clock vertical padding when pulsing. - */ - private int mPulsingPadding; - - /** * Doze/AOD transition amount. */ private float mDarkAmount; @@ -105,10 +100,6 @@ public class KeyguardClockPositionAlgorithm { */ private boolean mCurrentlySecure; - /** - * Dozing and receiving a notification (AOD notification.) - */ - private boolean mPulsing; private float mEmptyDragAmount; /** @@ -123,13 +114,11 @@ public class KeyguardClockPositionAlgorithm { R.dimen.burn_in_prevention_offset_x); mBurnInPreventionOffsetY = res.getDimensionPixelSize( R.dimen.burn_in_prevention_offset_y); - mPulsingPadding = res.getDimensionPixelSize( - R.dimen.widget_pulsing_bottom_padding); } public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, float dark, - boolean secure, boolean pulsing, float emptyDragAmount) { + boolean secure, float emptyDragAmount) { mMinTopMargin = minTopMargin + mContainerTopPadding; mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; @@ -138,7 +127,6 @@ public class KeyguardClockPositionAlgorithm { mKeyguardStatusHeight = keyguardStatusHeight; mDarkAmount = dark; mCurrentlySecure = secure; - mPulsing = pulsing; mEmptyDragAmount = emptyDragAmount; } @@ -146,7 +134,7 @@ public class KeyguardClockPositionAlgorithm { final int y = getClockY(); result.clockY = y; result.clockAlpha = getClockAlpha(y); - result.stackScrollerPadding = y + (mPulsing ? mPulsingPadding : mKeyguardStatusHeight); + result.stackScrollerPadding = y + mKeyguardStatusHeight; result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); } @@ -185,9 +173,6 @@ public class KeyguardClockPositionAlgorithm { private int getClockY() { // Dark: Align the bottom edge of the clock at about half of the screen: float clockYDark = getMaxClockY() + burnInPreventionOffsetY(); - if (mPulsing) { - clockYDark -= mPulsingPadding; - } clockYDark = MathUtils.max(0, clockYDark); float clockYRegular = getExpandedClockPosition(); @@ -230,11 +215,6 @@ public class KeyguardClockPositionAlgorithm { - mBurnInPreventionOffsetX; } - @VisibleForTesting - void setPulsingPadding(int padding) { - mPulsingPadding = padding; - } - public static class Result { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java index ebcd39b99778..323e7761b475 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java @@ -43,6 +43,11 @@ public class NavigationAssistantAction extends NavigationGestureAction { } @Override + public boolean disableProxyEvents() { + return true; + } + + @Override public void onGestureStart(MotionEvent event) { mAssistManager.startAssist(new Bundle()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java index 93605adf4589..7a42b03947ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java @@ -95,6 +95,11 @@ public class NavigationBackAction extends NavigationGestureAction { } } + @Override + public boolean disableProxyEvents() { + return true; + } + private void performBack() { sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); 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..0d5ebb9b6578 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); @@ -578,18 +579,23 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusView.getHeight(), mInterpolatedDarkAmount, mStatusBar.isKeyguardCurrentlySecure(), - mPulsing, mEmptyDragAmount); mClockPositionAlgorithm.run(mClockPositionResult); PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, 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; } mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); - mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX); + int burnInXOffset = mPulsing ? 0 : mClockPositionResult.clockX; + mNotificationStackScroller.setAntiBurnInOffsetX(burnInXOffset); mStackScrollerMeasuringPass++; requestScrollerTopPaddingUpdate(animate); @@ -2824,7 +2830,7 @@ public class NotificationPanelView extends PanelView implements mAnimateNextPositionUpdate = false; } mNotificationStackScroller.setPulsing(pulsing, animatePulse); - mKeyguardStatusView.setPulsing(pulsing, animatePulse); + mKeyguardStatusView.setPulsing(pulsing); mKeyguardBottomArea.setPulsing(pulsing, animatePulse); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index 0cec6371d813..9e91ab70e3de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -34,13 +34,20 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; +import android.hardware.input.InputManager; import android.os.RemoteException; +import android.os.SystemClock; import android.provider.Settings; import android.util.Log; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.ViewPropertyAnimator; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; + import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -64,7 +71,10 @@ public class QuickStepController implements GestureHelper { /** Experiment to swipe home button left to execute a back key press */ private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback"; + private static final String ENABLE_CLICK_THROUGH_NAV_PROP = "quickstepcontroller_clickthrough"; private static final long BACK_BUTTON_FADE_IN_ALPHA = 150; + private static final long CLICK_THROUGH_TAP_DELAY = 70; + private static final long CLICK_THROUGH_TAP_RESET_DELAY = 100; /** When the home-swipe-back gesture is disallowed, make it harder to pull */ private static final float HORIZONTAL_GESTURE_DAMPING = 0.3f; @@ -100,6 +110,9 @@ public class QuickStepController implements GestureHelper { private float mMinDragLimit; private float mDragDampeningFactor; private float mEdgeSwipeThreshold; + private boolean mClickThroughPressed; + private float mClickThroughPressX; + private float mClickThroughPressY; private NavigationGestureAction mCurrentAction; private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES]; @@ -117,6 +130,19 @@ public class QuickStepController implements GestureHelper { mOverviewEventSender = Dependency.get(OverviewProxyService.class); } + private final Runnable mClickThroughSendTap = new Runnable() { + @Override + public void run() { + sendTap(mClickThroughPressX, mClickThroughPressY); + mNavigationBarView.postDelayed(mClickThroughResetTap, CLICK_THROUGH_TAP_RESET_DELAY); + } + }; + + private final Runnable mClickThroughResetTap = () -> { + setWindowTouchable(true); + mClickThroughPressed = false; + }; + public void setComponents(NavigationBarView navigationBarView) { mNavigationBarView = navigationBarView; @@ -320,6 +346,25 @@ public class QuickStepController implements GestureHelper { case MotionEvent.ACTION_UP: if (mCurrentAction != null) { mCurrentAction.endGesture(); + } else if (action == MotionEvent.ACTION_UP + && getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP) + && !mClickThroughPressed) { + // Enable click through functionality where no gesture has been detected and not + // passed the drag slop so inject a touch event at the same location + // after making the navigation bar window untouchable. After a some time, the + // navigation bar will be able to take input events again + float diffX = Math.abs(event.getX() - mTouchDownX); + float diffY = Math.abs(event.getY() - mTouchDownY); + + if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx() + && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) { + setWindowTouchable(false); + mClickThroughPressX = event.getRawX(); + mClickThroughPressY = event.getRawY(); + mClickThroughPressed = true; + mNavigationBarView.postDelayed(mClickThroughSendTap, + CLICK_THROUGH_TAP_DELAY); + } } // Return the hit target back to its original position @@ -350,6 +395,19 @@ public class QuickStepController implements GestureHelper { return mCurrentAction != null || deadZoneConsumed; } + private void setWindowTouchable(boolean flag) { + final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) + ((ViewGroup) mNavigationBarView.getParent()).getLayoutParams(); + if (flag) { + lp.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE; + } else { + lp.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; + } + final WindowManager wm = (WindowManager) mNavigationBarView.getContext() + .getSystemService(Context.WINDOW_SERVICE); + wm.updateViewLayout((View) mNavigationBarView.getParent(), lp); + } + private boolean isEdgeSwipeAlongNavBar(int touchDown, boolean dragPositiveDirection) { // Detect edge swipe from side of 0 -> threshold if (dragPositiveDirection) { @@ -562,6 +620,38 @@ public class QuickStepController implements GestureHelper { return false; } + private void sendTap(float x, float y) { + long now = SystemClock.uptimeMillis(); + injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_DOWN, now, x, y, 1.0f); + injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_UP, now, x, y, 0.0f); + } + + private int getInputDeviceId(int inputSource) { + int[] devIds = InputDevice.getDeviceIds(); + for (int devId : devIds) { + InputDevice inputDev = InputDevice.getDevice(devId); + if (inputDev.supportsSource(inputSource)) { + return devId; + } + } + return 0; + } + + private void injectMotionEvent(int inputSource, int action, long when, float x, float y, + float pressure) { + final float defaultSize = 1.0f; + final int defaultMetaState = 0; + final float defaultPrecisionX = 1.0f; + final float defaultPrecisionY = 1.0f; + final int defaultEdgeFlags = 0; + MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, defaultSize, + defaultMetaState, defaultPrecisionX, defaultPrecisionY, + getInputDeviceId(inputSource), defaultEdgeFlags); + event.setSource(inputSource); + InputManager.getInstance().injectInputEvent(event, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + private boolean proxyMotionEvents(MotionEvent event) { final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy(); event.transform(mTransformGlobalMatrix); 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..bf143c8940e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -88,16 +88,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo /** * Default alpha value for most scrims. */ - public static final float GRADIENT_SCRIM_ALPHA = 0.45f; + public static final float GRADIENT_SCRIM_ALPHA = 0.2f; /** * A scrim varies its opacity based on a busyness factor, for example * how many notifications are currently visible. */ public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f; - /** - * Scrim opacity when a wallpaper doesn't support ambient mode. - */ - public static final float PULSING_WALLPAPER_SCRIM_ALPHA = 0.6f; /** * The most common scrim, the one under the keyguard. @@ -154,7 +150,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private Callback mCallback; private boolean mWallpaperSupportsAmbientMode; private boolean mScreenOn; - private float mNotificationDensity; // Scrim blanking callbacks private Runnable mPendingFrameCallback; @@ -245,7 +240,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mCurrentInFrontTint = state.getFrontTint(); mCurrentBehindTint = state.getBehindTint(); mCurrentInFrontAlpha = state.getFrontAlpha(); - mCurrentBehindAlpha = state.getBehindAlpha(mNotificationDensity); + mCurrentBehindAlpha = state.getBehindAlpha(); applyExpansionToAlpha(); // Scrim might acquire focus when user is navigating with a D-pad or a keyboard. @@ -279,8 +274,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Docking pulses may take a long time, wallpapers should also fade away after a while. if (mWallpaperSupportsAmbientMode && ( mDozeParameters.getAlwaysOn() && mState == ScrimState.AOD - || mState == ScrimState.PULSING && mCallback != null - && mCallback.isFadeOutWallpaper())) { + || mState == ScrimState.PULSING && mCallback != null)) { if (!mWallpaperVisibilityTimedOut) { mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); @@ -416,7 +410,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Either darken of make the scrim transparent when you // pull down the shade float interpolatedFract = getInterpolatedFraction(); - float alphaBehind = mState.getBehindAlpha(mNotificationDensity); + float alphaBehind = mState.getBehindAlpha(); if (mDarkenWhileDragging) { mCurrentBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind, interpolatedFract); @@ -430,24 +424,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } /** - * Keyguard and shade scrim opacity varies according to how many notifications are visible. - * @param notificationCount Number of visible notifications. - */ - public void setNotificationCount(int notificationCount) { - final float maxNotificationDensity = 3; - float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f); - if (mNotificationDensity == notificationDensity) { - return; - } - mNotificationDensity = notificationDensity; - - if (mState == ScrimState.KEYGUARD) { - applyExpansionToAlpha(); - scheduleUpdate(); - } - } - - /** * Sets the given drawable as the background of the scrim that shows up behind the * notifications. */ @@ -612,9 +588,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 +650,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) { @@ -898,7 +885,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Backdrop event may arrive after state was already applied, // in this case, back-scrim needs to be re-evaluated if (mState == ScrimState.AOD || mState == ScrimState.PULSING) { - float newBehindAlpha = mState.getBehindAlpha(mNotificationDensity); + float newBehindAlpha = mState.getBehindAlpha(); if (mCurrentBehindAlpha != newBehindAlpha) { mCurrentBehindAlpha = newBehindAlpha; updateScrims(); @@ -912,6 +899,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } } + public void setPulseReason(int pulseReason) { + ScrimState.PULSING.setPulseReason(pulseReason); + } + public interface Callback { default void onStart() { } @@ -921,9 +912,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } default void onCancelled() { } - default boolean isFadeOutWallpaper() { - return false; - } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 72519ba3503c..11a2d32c9dd6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.phone; import android.graphics.Color; import android.os.Trace; -import android.util.MathUtils; +import com.android.systemui.doze.DozeLog; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -57,13 +57,6 @@ public enum ScrimState { mCurrentBehindAlpha = mScrimBehindAlphaKeyguard; mCurrentInFrontAlpha = 0; } - - @Override - public float getBehindAlpha(float busynessFactor) { - return MathUtils.map(0 /* start */, 1 /* stop */, - mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, - busynessFactor); - } }, /** @@ -117,7 +110,7 @@ public enum ScrimState { } @Override - public float getBehindAlpha(float busyness) { + public float getBehindAlpha() { return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f; } @@ -133,17 +126,17 @@ public enum ScrimState { PULSING(5) { @Override public void prepare(ScrimState previousState) { - mCurrentInFrontAlpha = 0; - mCurrentInFrontTint = Color.BLACK; - mCurrentBehindTint = Color.BLACK; + mCurrentInFrontAlpha = 0f; + if (mPulseReason == DozeLog.PULSE_REASON_NOTIFICATION + || mPulseReason == DozeLog.PULSE_REASON_DOCKING) { + mCurrentBehindAlpha = previousState.getBehindAlpha(); + mCurrentBehindTint = Color.BLACK; + } else { + mCurrentBehindAlpha = mScrimBehindAlphaKeyguard; + mCurrentBehindTint = Color.TRANSPARENT; + } mBlankScreen = mDisplayRequiresBlanking; } - - @Override - public float getBehindAlpha(float busyness) { - return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f - : ScrimController.PULSING_WALLPAPER_SCRIM_ALPHA; - } }, /** @@ -204,6 +197,7 @@ public enum ScrimState { int mIndex; boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; + int mPulseReason; ScrimState(int index) { mIndex = index; @@ -235,7 +229,7 @@ public enum ScrimState { return mCurrentInFrontAlpha; } - public float getBehindAlpha(float busyness) { + public float getBehindAlpha() { return mCurrentBehindAlpha; } @@ -276,6 +270,10 @@ public enum ScrimState { mAodFrontScrimAlpha = aodFrontScrimAlpha; } + public void setPulseReason(int pulseReason) { + mPulseReason = pulseReason; + } + public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) { mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; } 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 7569a50a8c54..1470d0f44266 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); @@ -3873,6 +3883,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { + mScrimController.setPulseReason(reason); if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) { mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE"); startAssist(new Bundle()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 04d24dc18f05..4f61009095c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -346,7 +346,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void handleFullScreenIntent(NotificationEntry entry) { - boolean isHeadsUped = mNotificationInterruptionStateProvider.canHeadsUpCommon(entry); + boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry); if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) { if (shouldSuppressFullScreenIntent(entry)) { if (DEBUG) { 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/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java index 8ae7d52a5da5..ff30a4d6761d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java @@ -29,7 +29,10 @@ import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -37,6 +40,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.HashSet; @@ -45,14 +50,18 @@ import java.util.HashSet; @RunWithLooper public class NotificationRoundnessManagerTest extends SysuiTestCase { - private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager(); + private NotificationRoundnessManager mRoundnessManager; private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>(); private Runnable mRoundnessCallback = mock(Runnable.class); private ExpandableNotificationRow mFirst; private ExpandableNotificationRow mSecond; + @Mock + private AmbientPulseManager mAmbientPulseManager; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mRoundnessManager = new NotificationRoundnessManager(mAmbientPulseManager); com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); NotificationTestHelper testHelper = new NotificationTestHelper(getContext()); mFirst = testHelper.createRow(); @@ -127,6 +136,27 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { } @Test + public void testRoundnessPulsing() throws Exception { + // Let's create a notification that's neither the first or last item of the stack, + // this way we'll ensure that it won't have any rounded corners by default. + mRoundnessManager.updateRoundedChildren(new NotificationSection[]{ + createSection(mFirst, mSecond), + createSection(null, null) + }); + ExpandableNotificationRow row = new NotificationTestHelper(getContext()).createRow(); + NotificationEntry entry = mock(NotificationEntry.class); + when(entry.getRow()).thenReturn(row); + + mRoundnessManager.onAmbientStateChanged(entry, true); + Assert.assertEquals(1f, row.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(1f, row.getCurrentTopRoundness(), 0.0f); + + mRoundnessManager.onAmbientStateChanged(entry, false); + Assert.assertEquals(0f, row.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(0f, row.getCurrentTopRoundness(), 0.0f); + } + + @Test public void testRoundnessSetOnSecondSectionLast() { mRoundnessManager.updateRoundedChildren(new NotificationSection[]{ createSection(mFirst, mFirst), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index c140ba25859a..736f3840b91a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; @@ -35,17 +36,21 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.metrics.LogMaker; import android.provider.Settings; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.View; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.Dependency; import com.android.systemui.ExpandHelper; import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -73,6 +78,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -86,7 +92,8 @@ import java.util.ArrayList; @RunWith(AndroidJUnit4.class) public class NotificationStackScrollLayoutTest extends SysuiTestCase { - private NotificationStackScrollLayout mStackScroller; + private NotificationStackScrollLayout mStackScroller; // Normally test this + private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private StatusBar mBar; @@ -99,9 +106,12 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationData mNotificationData; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; + @Mock private MetricsLogger mMetricsLogger; + @Mock private NotificationRoundnessManager mNotificationRoundnessManager; private TestableNotificationEntryManager mEntryManager; private int mOriginalInterruptionModelSetting; + @Before @UiThreadTest public void setUp() throws Exception { @@ -110,6 +120,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { NotificationBlockingHelperManager.class, mBlockingHelperManager); mDependency.injectTestDependency(StatusBarStateController.class, mBarState); + mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager); mDependency.injectMockDependency(ShadeController.class); @@ -123,8 +134,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { NotificationShelf notificationShelf = mock(NotificationShelf.class); - mStackScroller = spy(new NotificationStackScrollLayout(getContext(), null, - true /* allowLongPress */)); + + // The actual class under test. You may need to work with this class directly when + // testing anonymous class members of mStackScroller, like mMenuEventListener, + // which refer to members of NotificationStackScrollLayout. The spy + // holds a copy of the CUT's instances of these classes, so they still refer to the CUT's + // member variables, not the spy's member variables. + mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, + true /* allowLongPress */, mNotificationRoundnessManager); + mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelf(notificationShelf); mStackScroller.setStatusBar(mBar); mStackScroller.setScrimController(mock(ScrimController.class)); @@ -422,6 +440,63 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertNull(swipeActionHelper.getExposedMenuView()); } + class LogMatcher implements ArgumentMatcher<LogMaker> { + private int mCategory, mType; + + LogMatcher(int category, int type) { + mCategory = category; + mType = type; + } + public boolean matches(LogMaker l) { + return (l.getCategory() == mCategory) + && (l.getType() == mType); + } + + public String toString() { + return String.format("LogMaker(%d, %d)", mCategory, mType); + } + } + + private LogMaker logMatcher(int category, int type) { + return argThat(new LogMatcher(category, type)); + } + + @Test + @UiThreadTest + public void testOnMenuClickedLogging() { + // Set up the object under test to have a valid mLongPressListener. We're testing an + // anonymous-class member, mMenuEventListener, so we need to modify the state of the + // class itself, not the Mockito spy copied from it. See notes in setup. + mStackScrollerInternal.setLongPressListener( + mock(ExpandableNotificationRow.LongPressListener.class)); + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock( + NotificationMenuRowPlugin.MenuItem.class)); + verify(row.getStatusBarNotification()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + @Test + @UiThreadTest + public void testOnMenuShownLogging() { + // Set up the object under test to have a valid mHeadsUpManager. See notes in setup. + mStackScrollerInternal.setHeadsUpManager(mHeadsUpManager); + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + mStackScroller.mMenuEventListener.onMenuShown(row); + verify(row.getStatusBarNotification()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + private void setBarStateForTest(int state) { // Can't inject this through the listener or we end up on the actual implementation // rather than the mock because the spy just coppied the anonymous inner /shruggie. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 27ed9c5a14c8..cd52e8792cfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -47,7 +47,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private float mPanelExpansion; private int mKeyguardStatusHeight; private float mDark; - private boolean mPulsing; @Before public void setUp() { @@ -208,20 +207,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { } @Test - public void notifPositionWhilePulsingOnAOD() { - // GIVEN on AOD and pulsing - givenAOD(); - mNotificationStackHeight = EMPTY_HEIGHT; - mKeyguardStatusHeight = EMPTY_HEIGHT; - mPulsing = true; - mClockPositionAlgorithm.setPulsingPadding(200); - // WHEN the clock position algorithm is run - positionClock(); - // THEN the notif padding doesn't adjust for pulsing. - assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000); - } - - @Test public void notifPositionMiddleOfScreenOnLockScreen() { // GIVEN on lock screen and both stack scroll and clock have 0 height givenLockScreen(); @@ -307,20 +292,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0); } - @Test - public void notifPositionWhilePulsingOnLockScreen() { - // GIVEN on lock screen and pulsing - givenLockScreen(); - mNotificationStackHeight = EMPTY_HEIGHT; - mKeyguardStatusHeight = EMPTY_HEIGHT; - mPulsing = true; - mClockPositionAlgorithm.setPulsingPadding(200); - // WHEN the clock position algorithm is run - positionClock(); - // THEN the notif padding adjusts for pulsing. - assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200); - } - private void givenAOD() { mPanelExpansion = 1.f; mDark = 1.f; @@ -334,7 +305,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void positionClock() { mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED, - mPulsing, ZERO_DRAG); + ZERO_DRAG); mClockPositionAlgorithm.run(mClockPosition); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 8eb42c4b088c..c20d37f123b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -45,6 +45,7 @@ import android.view.View; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.util.function.TriConsumer; import com.android.systemui.SysuiTestCase; +import com.android.systemui.doze.DozeLog; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.utils.os.FakeHandler; @@ -133,7 +134,8 @@ public class ScrimControllerTest extends SysuiTestCase { // Back scrim should be transparent assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT); - // Move on to PULSING and check if the back scrim is still transparent + // Pulsing notification should conserve AOD wallpaper. + mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION); mScrimController.transitionTo(ScrimState.PULSING); mScrimController.finishAnimationsImmediately(); assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT); @@ -218,13 +220,14 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.finishAnimationsImmediately(); assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE); + mScrimController.setPulseReason(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN); mScrimController.transitionTo(ScrimState.PULSING); mScrimController.finishAnimationsImmediately(); // Front scrim should be transparent // Back scrim should be semi-transparent so the user can see the wallpaper // Pulse callback should have been invoked assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT); - assertScrimTint(mScrimBehind, true /* tinted */); + assertScrimTint(mScrimBehind, false /* tinted */); } @Test @@ -477,12 +480,8 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testHoldsPulsingWallpaperAnimationLock() { // Pre-conditions - mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() { - @Override - public boolean isFadeOutWallpaper() { - return true; - } - }); + mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION); + mScrimController.transitionTo(ScrimState.PULSING); mScrimController.finishAnimationsImmediately(); reset(mWakeLock); @@ -491,7 +490,6 @@ public class ScrimControllerTest extends SysuiTestCase { verify(mWakeLock, never()).release(); mScrimController.finishAnimationsImmediately(); verify(mWakeLock).release(); - assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE); } @Test @@ -504,31 +502,27 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void testWillHidePulsingWallpaper_withRequestFadeOut() { - mScrimController.setWallpaperSupportsAmbientMode(true); - mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() { - @Override - public boolean isFadeOutWallpaper() { - return true; - } - }); - verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any()); - mScrimController.transitionTo(ScrimState.KEYGUARD); - verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); - } - - @Test - public void testDoesNotHidePulsingWallpaper_withoutRequestFadeOut() { - mScrimController.setWallpaperSupportsAmbientMode(true); - mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {}); - verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any()); + public void testWillHidePulsingWallpaper_whenNotification() { + mScrimController.setWallpaperSupportsAmbientMode(false); + mScrimController.transitionTo(ScrimState.AOD); + mScrimController.finishAnimationsImmediately(); + mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION); + mScrimController.transitionTo(ScrimState.PULSING); + mScrimController.finishAnimationsImmediately(); + assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE); + assertScrimTint(mScrimBehind, true); } @Test - public void testDoesNotHidePulsingWallpaper_withoutCallback() { - mScrimController.setWallpaperSupportsAmbientMode(true); + public void testWillHidePulsingWallpaper_whenDocking() { + mScrimController.setWallpaperSupportsAmbientMode(false); + mScrimController.transitionTo(ScrimState.AOD); + mScrimController.finishAnimationsImmediately(); + mScrimController.setPulseReason(DozeLog.PULSE_REASON_DOCKING); mScrimController.transitionTo(ScrimState.PULSING); - verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any()); + mScrimController.finishAnimationsImmediately(); + assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE); + assertScrimTint(mScrimBehind, true); } @Test @@ -560,34 +554,6 @@ public class ScrimControllerTest extends SysuiTestCase { Assert.assertTrue(mScrimController.wasAnimationJustCancelled()); } - /** - * Number of visible notifications affects scrim opacity. - */ - @Test - public void testNotificationDensity() { - mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.finishAnimationsImmediately(); - - mScrimController.setNotificationCount(0); - mScrimController.finishAnimationsImmediately(); - Assert.assertEquals("lower density when no notifications", - ScrimController.GRADIENT_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.01f); - - mScrimController.setNotificationCount(3); - mScrimController.finishAnimationsImmediately(); - Assert.assertEquals("stronger density when notifications are visible", - ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mScrimBehind.getViewAlpha(), 0.01f); - } - - /** - * Moving from/to states conserves old notification density. - */ - @Test - public void testConservesNotificationDensity() { - testConservesNotificationDensity(0 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA); - testConservesNotificationDensity(3 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY); - } - @Test public void testScrimFocus() { mScrimController.transitionTo(ScrimState.AOD); @@ -662,24 +628,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimBehind.getDefaultFocusHighlightEnabled()); } - /** - * Conserves old notification density after leaving state and coming back. - * - * @param count How many notification. - * @param expectedAlpha Expected alpha. - */ - private void testConservesNotificationDensity(int count, float expectedAlpha) { - mScrimController.setNotificationCount(count); - mScrimController.transitionTo(ScrimState.UNLOCKED); - mScrimController.finishAnimationsImmediately(); - - mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.finishAnimationsImmediately(); - - Assert.assertEquals("Doesn't respect notification busyness after transition", - expectedAlpha, mScrimBehind.getViewAlpha(), 0.01f); - } - private void assertScrimTint(ScrimView scrimView, boolean tinted) { final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT; final String name = scrimView == mScrimInFront ? "front" : "back"; diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index a07411d0afaf..8261fe89f778 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6814,6 +6814,12 @@ message MetricsEvent { // OS: Q SETTINGS_GESTURE_TAP_SCREEN = 1626; + // OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of + // SIM/eSIM subscriptions. + // CATEGORY: SETTINGS + // OS: Q + MOBILE_NETWORK_LIST = 1627; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index e9ce737eb5a5..bcc43a763599 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -485,6 +485,9 @@ message WifiLog { // Multiple lists of timestamped link layer stats with labels to represent whether wifi is usable repeated WifiUsabilityStats wifi_usability_stats_list = 126; + + // Counts the occurrences of each Wifi usability score provided by external app + repeated WifiUsabilityScoreCount wifi_usability_score_count = 127; } // Information that gets logged for every WiFi connection. @@ -670,6 +673,15 @@ message WifiScoreCount { optional int32 count = 2; } +// Counts the number of instances of a specific Wifi Usability Score +message WifiUsabilityScoreCount { + // Wifi Usability Score + optional int32 score = 1; + + // Number of Wifi score reports with this score + optional int32 count = 2; +} + // Number of occurrences of a specific link speed (Mbps) // and sum of rssi (dBm) and rssi^2 (dBm^2) message LinkSpeedCount { @@ -826,6 +838,10 @@ message StaEvent { // Wifi is turned off TYPE_WIFI_DISABLED = 19; + + // The NetworkAgent Wifi usability score has changed in a way that may + // impact connectivity + TYPE_WIFI_USABILITY_SCORE_BREACH = 20; } enum FrameworkDisconnectReason { @@ -940,6 +956,9 @@ message StaEvent { // NetworkAgent score of connected wifi optional int32 last_score = 14 [default = -1]; + + // NetworkAgent Wifi usability score of connected wifi + optional int32 last_wifi_usability_score = 15 [default = -1]; } // Wi-Fi Aware metrics @@ -1653,6 +1672,10 @@ message WifiIsUnusableEvent { // Firmware alert code. Only valid when the event was triggered by a firmware alert, otherwise -1. optional int32 firmware_alert_code = 10 [default = -1]; + + // NetworkAgent wifi usability score of connected wifi. + // Defaults to -1 if the score was never set. + optional int32 last_wifi_usability_score = 11 [default = -1]; } message PasspointProfileTypeCount { @@ -1765,6 +1788,15 @@ message WifiUsabilityStatsEntry { // The total time spent on hotspot2.0 scans and GAS exchange in ms counted from the last radio // chip reset optional int64 total_hotspot_2_scan_time_ms = 16; + + // Internal framework Wifi score + optional int32 wifi_score = 17; + + // Wifi usability score provided by external system app + optional int32 wifi_usability_score = 18; + + // Sequence number from external system app to framework + optional int32 seq_num_to_framework = 19; } message WifiUsabilityStats { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 36ca52a6e600..763c16f771d0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -16,6 +16,11 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; @@ -23,13 +28,9 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBIL import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; + import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; import android.Manifest; import android.accessibilityservice.AccessibilityService; @@ -121,6 +122,8 @@ import com.android.server.SystemService; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; +import libcore.util.EmptyArray; + import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; @@ -139,8 +142,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.IntSupplier; -import libcore.util.EmptyArray; - /** * This class is instantiated by the system as a system level service and can be * accessed only by the system. The task of this service is to be a centralized @@ -1827,8 +1828,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub updateFilterKeyEventsLocked(userState); updateTouchExplorationLocked(userState); updatePerformGesturesLocked(userState); - updateDisplayDaltonizerLocked(userState); - updateDisplayInversionLocked(userState); updateMagnificationLocked(userState); scheduleUpdateFingerprintGestureHandling(userState); scheduleUpdateInputFilter(userState); @@ -2187,14 +2186,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private void updateDisplayDaltonizerLocked(UserState userState) { - DisplayAdjustmentUtils.applyDaltonizerSetting(mContext, userState.mUserId); - } - - private void updateDisplayInversionLocked(UserState userState) { - DisplayAdjustmentUtils.applyInversionSetting(mContext, userState.mUserId); - } - private void updateMagnificationLocked(UserState userState) { if (userState.mUserId != mCurrentUserId) { return; @@ -4104,15 +4095,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mTouchExplorationGrantedAccessibilityServicesUri = Settings.Secure .getUriFor(Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES); - private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); - - private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); - - private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER); - private final Uri mHighTextContrastUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED); @@ -4153,12 +4135,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mTouchExplorationGrantedAccessibilityServicesUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( - mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( - mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( - mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( mHighTextContrastUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL); @@ -4202,11 +4178,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (readTouchExplorationGrantedAccessibilityServicesLocked(userState)) { onUserStateChangedLocked(userState); } - } else if (mDisplayDaltonizerEnabledUri.equals(uri) - || mDisplayDaltonizerUri.equals(uri)) { - updateDisplayDaltonizerLocked(userState); - } else if (mDisplayInversionEnabledUri.equals(uri)) { - updateDisplayInversionLocked(userState); } else if (mHighTextContrastUri.equals(uri)) { if (readHighTextContrastEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java deleted file mode 100644 index c81a876fe39e..000000000000 --- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.accessibility; - -import android.content.ContentResolver; -import android.content.Context; -import android.os.Binder; -import android.provider.Settings.Secure; -import android.view.accessibility.AccessibilityManager; - -import com.android.server.LocalServices; -import com.android.server.display.DisplayTransformManager; - -/** - * Utility methods for performing accessibility display adjustments. - */ -class DisplayAdjustmentUtils { - - /** Default inversion mode for display color correction. */ - private static final int DEFAULT_DISPLAY_DALTONIZER = - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY; - - /** Matrix and offset used for converting color to gray-scale. */ - private static final float[] MATRIX_GRAYSCALE = new float[] { - .2126f, .2126f, .2126f, 0, - .7152f, .7152f, .7152f, 0, - .0722f, .0722f, .0722f, 0, - 0, 0, 0, 1 - }; - - /** - * Matrix and offset used for luminance inversion. Represents a transform - * from RGB to YIQ color space, rotation around the Y axis by 180 degrees, - * transform back to RGB color space, and subtraction from 1. The last row - * represents a non-multiplied addition, see surfaceflinger's ProgramCache - * for full implementation details. - */ - private static final float[] MATRIX_INVERT_COLOR = new float[] { - 0.402f, -0.598f, -0.599f, 0, - -1.174f, -0.174f, -1.175f, 0, - -0.228f, -0.228f, 0.772f, 0, - 1, 1, 1, 1 - }; - - public static void applyDaltonizerSetting(Context context, int userId) { - final ContentResolver cr = context.getContentResolver(); - final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class); - - int daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED; - long identity = Binder.clearCallingIdentity(); - try { - if (Secure.getIntForUser(cr, - Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) { - daltonizerMode = Secure.getIntForUser(cr, - Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, userId); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - - float[] grayscaleMatrix = null; - if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) { - // Monochromacy isn't supported by the native Daltonizer. - grayscaleMatrix = MATRIX_GRAYSCALE; - daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED; - } - dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, grayscaleMatrix); - dtm.setDaltonizerMode(daltonizerMode); - } - - /** - * Applies the specified user's display color adjustments. - */ - public static void applyInversionSetting(Context context, int userId) { - final ContentResolver cr = context.getContentResolver(); - final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class); - - long identity = Binder.clearCallingIdentity(); - try { - final boolean invertColors = Secure.getIntForUser(cr, - Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0; - dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR, - invertColors ? MATRIX_INVERT_COLOR : null); - } finally { - Binder.restoreCallingIdentity(identity); - } - } -} 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 ec6d20dd5c6a..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; diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index d037b081cd4d..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; @@ -310,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/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index a5ef21afc23e..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 diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index e0e81ffb6b48..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.restoreconRecursive(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.restoreconRecursive(mDataDir)) { - Slog.w(TAG, "SELinux restorecon failed on " + mDataDir); - } - } mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng); // Receivers for scheduled backups and transport initialization operations. 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/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 1dae2ceae7d9..8d2c79bd9923 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -85,7 +85,6 @@ final class ContentCapturePerUserService ContentCapturePerUserService(@NonNull ContentCaptureManagerService master, @NonNull Object lock, boolean disabled, @UserIdInt int userId) { super(master, lock, userId); - updateRemoteServiceLocked(disabled); } @@ -170,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, 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 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/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d6f3e2ba4835..00550d9c660e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1494,6 +1494,9 @@ public class ConnectivityService extends IConnectivityManager.Stub newNc.setUids(null); newNc.setSSID(null); } + if (newNc.getNetworkSpecifier() != null) { + newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact()); + } return newNc; } @@ -5358,7 +5361,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } switch (notificationType) { case ConnectivityManager.CALLBACK_AVAILABLE: { - putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities)); + putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions( + networkAgent.networkCapabilities, nri.mPid, nri.mUid)); putParcelable(bundle, new LinkProperties(networkAgent.linkProperties)); // For this notification, arg1 contains the blocked status. msg.arg1 = arg1; diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 0b4c01ef90bd..add5e5feaa8a 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -46,7 +46,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.content.res.Resources; import android.database.ContentObserver; -import android.hardware.location.ActivityRecognitionHardware; import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; @@ -93,7 +92,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.location.AbstractLocationProvider; -import com.android.server.location.ActivityRecognitionProxy; import com.android.server.location.GeocoderProxy; import com.android.server.location.GeofenceManager; import com.android.server.location.GeofenceProxy; @@ -738,25 +736,6 @@ public class LocationManagerService extends ILocationManager.Stub { Slog.d(TAG, "Unable to bind FLP Geofence proxy."); } - // bind to hardware activity recognition - boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported(); - ActivityRecognitionHardware activityRecognitionHardware = null; - if (activityRecognitionHardwareIsSupported) { - activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext); - } else { - Slog.d(TAG, "Hardware Activity-Recognition not supported."); - } - ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind( - mContext, - activityRecognitionHardwareIsSupported, - activityRecognitionHardware, - com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay, - com.android.internal.R.string.config_activityRecognitionHardwarePackageName, - com.android.internal.R.array.config_locationProviderPackageNames); - if (proxy == null) { - Slog.d(TAG, "Unable to bind ActivityRecognitionProxy."); - } - String[] testProviderStrings = resources.getStringArray( com.android.internal.R.array.config_testLocationProviders); for (String testProviderString : testProviderStrings) { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 2a806447d605..db2a7331bd7f 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -780,6 +780,13 @@ class StorageManagerService extends IStorageManager.Stub }); refreshZramSettings(); + // Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled + String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY); + if (!zramPropValue.equals("0") + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_zramWriteback)) { + ZramWriteback.scheduleZramWriteback(mContext); + } // Toggle isolated-enable system property in response to settings mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE), @@ -813,6 +820,12 @@ class StorageManagerService extends IStorageManager.Stub // changing the property value. There's no race: we're the // sole writer. SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue); + // Schedule writeback only if zram is being enabled. + if (desiredPropertyValue.equals("1") + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_zramWriteback)) { + ZramWriteback.scheduleZramWriteback(mContext); + } } } diff --git a/services/core/java/com/android/server/ZramWriteback.java b/services/core/java/com/android/server/ZramWriteback.java new file mode 100644 index 000000000000..3a4aff2ea9aa --- /dev/null +++ b/services/core/java/com/android/server/ZramWriteback.java @@ -0,0 +1,187 @@ +/* + * 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; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; +import android.content.Context; +import android.os.FileUtils; +import android.os.SystemProperties; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * Schedules jobs for triggering zram writeback. + */ +public final class ZramWriteback extends JobService { + private static final String TAG = "ZramWriteback"; + private static final boolean DEBUG = false; + + private static final ComponentName sZramWriteback = + new ComponentName("android", ZramWriteback.class.getName()); + + private static final int MARK_IDLE_JOB_ID = 811; + private static final int WRITEBACK_IDLE_JOB_ID = 812; + + private static final int MAX_ZRAM_DEVICES = 256; + private static int sZramDeviceId = 0; + + private static final String IDLE_SYS = "/sys/block/zram%d/idle"; + private static final String IDLE_SYS_ALL_PAGES = "all"; + + private static final String WB_SYS = "/sys/block/zram%d/writeback"; + private static final String WB_SYS_IDLE_PAGES = "idle"; + + private static final String WB_STATS_SYS = "/sys/block/zram%d/bd_stat"; + private static final int WB_STATS_MAX_FILE_SIZE = 128; + + private static final String BDEV_SYS = "/sys/block/zram%d/backing_dev"; + + private static final String MARK_IDLE_DELAY_PROP = "ro.zram.mark_idle_delay_mins"; + private static final String FIRST_WB_DELAY_PROP = "ro.zram.first_wb_delay_mins"; + private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours"; + + private void markPagesAsIdle() { + String idlePath = String.format(IDLE_SYS, sZramDeviceId); + try { + FileUtils.stringToFile(new File(idlePath), IDLE_SYS_ALL_PAGES); + } catch (IOException e) { + Slog.e(TAG, "Failed to write to " + idlePath); + } + } + + private void flushIdlePages() { + if (DEBUG) Slog.d(TAG, "Start writing back idle pages to disk"); + String wbPath = String.format(WB_SYS, sZramDeviceId); + try { + FileUtils.stringToFile(new File(wbPath), WB_SYS_IDLE_PAGES); + } catch (IOException e) { + Slog.e(TAG, "Failed to write to " + wbPath); + } + if (DEBUG) Slog.d(TAG, "Finished writeback back idle pages"); + } + + private int getWrittenPageCount() { + String wbStatsPath = String.format(WB_STATS_SYS, sZramDeviceId); + try { + String wbStats = FileUtils + .readTextFile(new File(wbStatsPath), WB_STATS_MAX_FILE_SIZE, ""); + return Integer.parseInt(wbStats.trim().split("\\s+")[2], 10); + } catch (IOException e) { + Slog.e(TAG, "Failed to read writeback stats from " + wbStatsPath); + } + + return -1; + } + + private void markAndFlushPages() { + int pageCount = getWrittenPageCount(); + + flushIdlePages(); + markPagesAsIdle(); + + if (pageCount != -1) { + Slog.i(TAG, "Total pages written to disk is " + (getWrittenPageCount() - pageCount)); + } + } + + private static boolean isWritebackEnabled() { + try { + String backingDev = FileUtils + .readTextFile(new File(String.format(BDEV_SYS, sZramDeviceId)), 128, ""); + if (!"none".equals(backingDev.trim())) { + return true; + } else { + Slog.w(TAG, "Writeback device is not set"); + } + } catch (IOException e) { + Slog.w(TAG, "Writeback is not enabled on zram"); + } + return false; + } + + private static void schedNextWriteback(Context context) { + int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24); + JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + + js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback) + .setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay)) + .setRequiresDeviceIdle(true) + .build()); + } + + @Override + public boolean onStartJob(JobParameters params) { + + if (!isWritebackEnabled()) { + jobFinished(params, false); + return false; + } + + if (params.getJobId() == MARK_IDLE_JOB_ID) { + markPagesAsIdle(); + jobFinished(params, false); + return false; + } else { + new Thread("ZramWriteback_WritebackIdlePages") { + @Override + public void run() { + markAndFlushPages(); + schedNextWriteback(ZramWriteback.this); + jobFinished(params, false); + } + }.start(); + } + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + // The thread that triggers the writeback is non-interruptible + return false; + } + + /** + * Schedule the zram writeback job to trigger a writeback when idle + */ + public static void scheduleZramWriteback(Context context) { + int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20); + int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180); + + JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + + // Schedule a one time job to mark pages as idle. These pages will be written + // back at later point if they remain untouched. + js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback) + .setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay)) + .build()); + + // Schedule a one time job to flush idle pages to disk. + // After the initial writeback, subsequent writebacks are done at interval set + // by ro.zram.periodic_wb_delay_hours. + js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback) + .setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay)) + .setRequiresDeviceIdle(true) + .build()); + } +} diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 1f9362e246b7..dc033691eea5 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -132,8 +132,11 @@ public final class OomAdjuster { mActiveUids = activeUids; mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); - mAppCompact = new AppCompactor(mService); mConstants = mService.mConstants; + // mConstants can be null under test, which causes AppCompactor to crash + if (mConstants != null) { + mAppCompact = new AppCompactor(mService); + } } /** diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 5bc88450c376..c1be3872e031 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1678,12 +1678,14 @@ public final class ProcessList { AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid); final ArrayList<ProcessRecord> zygoteProcessList; if (appZygote == null) { - final int userId = UserHandle.getUserId(app.info.uid); final IsolatedUidRange uidRange = mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info); - // Allocate an isolated UID out of this range for the Zygote itself - final int zygoteIsolatedUid = uidRange.allocateIsolatedUidLocked(userId); - appZygote = new AppZygote(app.info, zygoteIsolatedUid); + final int userId = UserHandle.getUserId(app.info.uid); + // Create the app-zygote and provide it with the UID-range it's allowed + // to setresuid/setresgid to. + final int firstUid = UserHandle.getUid(userId, uidRange.mFirstUid); + final int lastUid = UserHandle.getUid(userId, uidRange.mLastUid); + appZygote = new AppZygote(app.info, app.info.uid, firstUid, lastUid); mAppZygotes.put(app.info.processName, app.info.uid, appZygote); zygoteProcessList = new ArrayList<ProcessRecord>(); mAppZygoteProcesses.put(appZygote, zygoteProcessList); diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java new file mode 100644 index 000000000000..f60d6b06f451 --- /dev/null +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -0,0 +1,548 @@ +/* + * 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.attention; + +import android.Manifest; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.attention.AttentionManagerInternal; +import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.service.attention.AttentionService; +import android.service.attention.AttentionService.AttentionFailureCodes; +import android.service.attention.IAttentionCallback; +import android.service.attention.IAttentionService; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; +import com.android.server.SystemService; + +import java.io.PrintWriter; + +/** + * An attention service implementation that runs in System Server process. + * This service publishes a LocalService and reroutes calls to a {@link AttentionService} that it + * manages. + */ +public class AttentionManagerService extends SystemService { + private static final String LOG_TAG = "AttentionManagerService"; + + /** Service will unbind if connection is not used for that amount of time. */ + private static final long CONNECTION_TTL_MILLIS = 60_000; + + /** If the check attention called within that period - cached value will be returned. */ + private static final long STALE_AFTER_MILLIS = 5_000; + + private final Context mContext; + private final PowerManager mPowerManager; + private final ActivityManager mActivityManager; + private final Object mLock; + @GuardedBy("mLock") + private final SparseArray<UserState> mUserStates = new SparseArray<>(); + private final AttentionHandler mAttentionHandler; + + private ComponentName mComponentName; + + public AttentionManagerService(Context context) { + super(context); + mContext = Preconditions.checkNotNull(context); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mLock = new Object(); + mAttentionHandler = new AttentionHandler(); + } + + @Override + public void onStart() { + publishLocalService(AttentionManagerInternal.class, new LocalService()); + } + + @Override + public void onStopUser(int userId) { + cancelAndUnbindLocked(peekUserStateLocked(userId), + AttentionService.ATTENTION_FAILURE_UNKNOWN); + } + + @Override + public void onBootPhase(int phase) { + super.onBootPhase(phase); + if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { + mComponentName = resolveAttentionService(mContext); + if (mComponentName != null) { + // If the service is supported we want to keep receiving the screen off events. + mContext.registerReceiver(new ScreenStateReceiver(), + new IntentFilter(Intent.ACTION_SCREEN_OFF)); + } + } + } + + /** + * Returns {@code true} if attention service is supported on this device. + */ + public boolean isAttentionServiceSupported() { + return mComponentName != null; + } + + /** + * Checks whether user attention is at the screen and calls in the provided callback. + * + * @return {@code true} if the framework was able to send the provided callback to the service + */ + public boolean checkAttention(int requestCode, long timeout, + AttentionCallbackInternal callback) { + Preconditions.checkNotNull(callback); + + if (!isAttentionServiceSupported()) { + Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device."); + return false; + } + + // don't allow attention check in screen off state + if (!mPowerManager.isInteractive()) { + return false; + } + + synchronized (mLock) { + unbindAfterTimeoutLocked(); + + final UserState userState = getOrCreateCurrentUserStateLocked(); + // lazily start the service, which should be very lightweight to start + if (!userState.bindLocked()) { + return false; + } + + if (userState.mService == null) { + // make sure every callback is called back + if (userState.mPendingAttentionCheck != null) { + userState.mPendingAttentionCheck.cancel( + AttentionService.ATTENTION_FAILURE_UNKNOWN); + } + userState.mPendingAttentionCheck = new PendingAttentionCheck(requestCode, + callback, () -> checkAttention(requestCode, timeout, callback)); + } else { + try { + // throttle frequent requests + final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache; + if (attentionCheckCache != null && SystemClock.uptimeMillis() + < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) { + callback.onSuccess(requestCode, attentionCheckCache.mResult, + attentionCheckCache.mTimestamp); + return true; + } + + cancelAfterTimeoutLocked(timeout); + + userState.mCurrentAttentionCheckRequestCode = requestCode; + userState.mService.checkAttention(requestCode, new IAttentionCallback.Stub() { + @Override + public void onSuccess(int requestCode, int result, long timestamp) { + callback.onSuccess(requestCode, result, timestamp); + userState.mAttentionCheckCache = new AttentionCheckCache( + SystemClock.uptimeMillis(), result, + timestamp); + } + + @Override + public void onFailure(int requestCode, int error) { + callback.onFailure(requestCode, error); + } + + @Override + public IBinder asBinder() { + return null; + } + }); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Cannot call into the AttentionService"); + return false; + } + } + return true; + } + } + + /** Cancels the specified attention check. */ + public void cancelAttentionCheck(int requestCode) { + final UserState userState = getOrCreateCurrentUserStateLocked(); + try { + userState.mService.cancelAttentionCheck(requestCode); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Cannot call into the AttentionService"); + } + } + + @GuardedBy("mLock") + private void unbindAfterTimeoutLocked() { + mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED, + CONNECTION_TTL_MILLIS); + } + + @GuardedBy("mLock") + private void cancelAfterTimeoutLocked(long timeout) { + mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.ATTENTION_CHECK_TIMEOUT, + timeout); + } + + + @GuardedBy("mLock") + private UserState getOrCreateCurrentUserStateLocked() { + return getOrCreateUserStateLocked(mActivityManager.getCurrentUser()); + } + + @GuardedBy("mLock") + private UserState getOrCreateUserStateLocked(int userId) { + UserState result = mUserStates.get(userId); + if (result == null) { + result = new UserState(userId, mContext, mLock); + mUserStates.put(userId, result); + } + return result; + } + + @GuardedBy("mLock") + UserState peekCurrentUserStateLocked() { + return peekUserStateLocked(mActivityManager.getCurrentUser()); + } + + @GuardedBy("mLock") + UserState peekUserStateLocked(int userId) { + return mUserStates.get(userId); + } + + /** + * Provides attention service component name at runtime, making sure it's provided by the + * system. + */ + private static ComponentName resolveAttentionService(Context context) { + // TODO(b/111939367): add a flag to turn on/off. + final String componentNameString = context.getString( + R.string.config_defaultAttentionService); + + if (TextUtils.isEmpty(componentNameString)) { + return null; + } + + final ComponentName componentName = ComponentName.unflattenFromString(componentNameString); + if (componentName == null) { + return null; + } + + final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage( + componentName.getPackageName()); + + // Make sure that only system apps can declare the AttentionService. + final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, + PackageManager.MATCH_SYSTEM_ONLY); + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s", + AttentionService.SERVICE_INTERFACE, componentName + )); + return null; + } + + final ServiceInfo serviceInfo = resolveInfo.serviceInfo; + final String permission = serviceInfo.permission; + if (Manifest.permission.BIND_ATTENTION_SERVICE.equals(permission)) { + return serviceInfo.getComponentName(); + } + Slog.e(LOG_TAG, String.format( + "Service %s should require %s permission. Found %s permission", + serviceInfo.getComponentName(), + Manifest.permission.BIND_ATTENTION_SERVICE, + serviceInfo.permission)); + return null; + } + + private void dumpInternal(PrintWriter pw) { + if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println("Attention Manager Service (dumpsys attention)\n"); + + ipw.printPair("context", mContext); + pw.println(); + synchronized (mLock) { + int size = mUserStates.size(); + ipw.print("Number user states: "); + pw.println(size); + if (size > 0) { + ipw.increaseIndent(); + for (int i = 0; i < size; i++) { + UserState userState = mUserStates.valueAt(i); + ipw.print(i); + ipw.print(":"); + userState.dump(ipw); + ipw.println(); + } + ipw.decreaseIndent(); + } + } + } + + private final class LocalService extends AttentionManagerInternal { + @Override + public boolean isAttentionServiceSupported() { + return AttentionManagerService.this.isAttentionServiceSupported(); + } + + @Override + public boolean checkAttention(int requestCode, long timeout, + AttentionCallbackInternal callback) { + return AttentionManagerService.this.checkAttention(requestCode, timeout, callback); + } + + @Override + public void cancelAttentionCheck(int requestCode) { + AttentionManagerService.this.cancelAttentionCheck(requestCode); + } + } + + private static final class AttentionCheckCache { + private final long mLastComputed; + private final int mResult; + private final long mTimestamp; + + AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result, + long timestamp) { + mLastComputed = lastComputed; + mResult = result; + mTimestamp = timestamp; + } + } + + private static final class PendingAttentionCheck { + private final int mRequestCode; + private final AttentionCallbackInternal mCallback; + private final Runnable mRunnable; + + PendingAttentionCheck(int requestCode, AttentionCallbackInternal callback, + Runnable runnable) { + mRequestCode = requestCode; + mCallback = callback; + mRunnable = runnable; + } + + void cancel(@AttentionFailureCodes int failureCode) { + mCallback.onFailure(mRequestCode, failureCode); + } + + void run() { + mRunnable.run(); + } + } + + private static final class UserState { + final AttentionServiceConnection mConnection = new AttentionServiceConnection(); + + @GuardedBy("mLock") + IAttentionService mService; + @GuardedBy("mLock") + boolean mBinding; + @GuardedBy("mLock") + int mCurrentAttentionCheckRequestCode; + @GuardedBy("mLock") + PendingAttentionCheck mPendingAttentionCheck; + + @GuardedBy("mLock") + AttentionCheckCache mAttentionCheckCache; + + @UserIdInt + final int mUserId; + final Context mContext; + final Object mLock; + + private UserState(int userId, Context context, Object lock) { + mUserId = userId; + mContext = Preconditions.checkNotNull(context); + mLock = Preconditions.checkNotNull(lock); + } + + + @GuardedBy("mLock") + private void handlePendingCallbackLocked() { + if (mService != null && mPendingAttentionCheck != null) { + mPendingAttentionCheck.run(); + mPendingAttentionCheck = null; + } + } + + /** Binds to the system's AttentionService which provides an actual implementation. */ + @GuardedBy("mLock") + private boolean bindLocked() { + // No need to bind if service is binding or has already been bound. + if (mBinding || mService != null) { + return true; + } + + final boolean willBind; + final long identity = Binder.clearCallingIdentity(); + + try { + final ComponentName componentName = + resolveAttentionService(mContext); + if (componentName == null) { + // Might happen if the storage is encrypted and the user is not unlocked + return false; + } + final Intent mServiceIntent = new Intent( + AttentionService.SERVICE_INTERFACE).setComponent( + componentName); + willBind = mContext.bindServiceAsUser(mServiceIntent, mConnection, + Context.BIND_AUTO_CREATE, UserHandle.CURRENT); + mBinding = willBind; + } finally { + Binder.restoreCallingIdentity(identity); + } + return willBind; + } + + private void dump(IndentingPrintWriter pw) { + pw.printPair("context", mContext); + pw.printPair("userId", mUserId); + synchronized (mLock) { + pw.printPair("binding", mBinding); + pw.printPair("isAttentionCheckPending", mPendingAttentionCheck != null); + } + } + + private final class AttentionServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + init(IAttentionService.Stub.asInterface(service)); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + cleanupService(); + } + + @Override + public void onBindingDied(ComponentName name) { + cleanupService(); + } + + @Override + public void onNullBinding(ComponentName name) { + cleanupService(); + } + + void cleanupService() { + init(null); + } + + private void init(@Nullable IAttentionService service) { + synchronized (mLock) { + mService = service; + mBinding = false; + handlePendingCallbackLocked(); + } + } + } + } + + private class AttentionHandler extends Handler { + private static final int CONNECTION_EXPIRED = 1; + private static final int ATTENTION_CHECK_TIMEOUT = 2; + + AttentionHandler() { + super(Looper.myLooper()); + } + + public void handleMessage(Message msg) { + switch (msg.what) { + // Do not occupy resources when not in use - unbind proactively. + case CONNECTION_EXPIRED: { + for (int i = 0; i < mUserStates.size(); i++) { + cancelAndUnbindLocked(mUserStates.valueAt(i), + AttentionService.ATTENTION_FAILURE_UNKNOWN); + } + + } + break; + + // Callee is no longer interested in the attention check result - cancel. + case ATTENTION_CHECK_TIMEOUT: { + cancelAndUnbindLocked(peekCurrentUserStateLocked(), + AttentionService.ATTENTION_FAILURE_TIMED_OUT); + } + break; + + default: + break; + } + } + } + + @GuardedBy("mLock") + private void cancelAndUnbindLocked(UserState userState, + @AttentionFailureCodes int failureCode) { + synchronized (mLock) { + if (userState != null && userState.mService != null) { + try { + userState.mService.cancelAttentionCheck( + userState.mCurrentAttentionCheckRequestCode); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Unable to cancel attention check"); + } + + if (userState.mPendingAttentionCheck != null) { + userState.mPendingAttentionCheck.cancel(failureCode); + } + mContext.unbindService(userState.mConnection); + userState.mConnection.cleanupService(); + mUserStates.remove(userState.mUserId); + } + } + } + + /** + * Unbinds and stops the service when the screen off intent is received. + * Attention service only makes sense when screen is ON; disconnect and stop service otherwise. + */ + private final class ScreenStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + cancelAndUnbindLocked(peekCurrentUserStateLocked(), + AttentionService.ATTENTION_FAILURE_UNKNOWN); + } + } + } +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 9a4ca1f954d0..d9fcf9ed8a7f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1614,7 +1614,9 @@ public class AudioService extends IAudioService.Stub // For notifications/ring, show the ui before making any adjustments // Don't suppress mute/unmute requests - if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) { + // Don't suppress adjustments for single volume device + if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute) + && !mIsSingleVolume) { direction = 0; flags &= ~AudioManager.FLAG_PLAY_SOUND; flags &= ~AudioManager.FLAG_VIBRATE; 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/display/AppSaturationController.java b/services/core/java/com/android/server/display/AppSaturationController.java new file mode 100644 index 000000000000..5d5e4f70ea7f --- /dev/null +++ b/services/core/java/com/android/server/display/AppSaturationController.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.annotation.UserIdInt; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.ColorDisplayService.ColorTransformController; + +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +class AppSaturationController { + + private final Object mLock = new Object(); + + /** + * A package name has one or more userIds it is running under. Each userId has zero or one + * saturation level, and zero or more ColorTransformControllers. + */ + @GuardedBy("mLock") + private final Map<String, SparseArray<SaturationController>> mAppsMap = new HashMap<>(); + + @VisibleForTesting + static final float[] TRANSLATION_VECTOR = {0f, 0f, 0f}; + + /** + * Add an {@link WeakReference<ColorTransformController>} for a given package and userId. + */ + boolean addColorTransformController(String packageName, @UserIdInt int userId, + WeakReference<ColorTransformController> controller) { + synchronized (mLock) { + return getSaturationControllerLocked(packageName, userId) + .addColorTransformController(controller); + } + } + + /** + * Set the saturation level ({@code ColorDisplayManager#SaturationLevel} constant for a given + * package name and userId. + */ + public boolean setSaturationLevel(String packageName, @UserIdInt int userId, + int saturationLevel) { + synchronized (mLock) { + return getSaturationControllerLocked(packageName, userId) + .setSaturationLevel(saturationLevel); + } + } + + /** + * Dump state information. + */ + public void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println("App Saturation: "); + if (mAppsMap.size() == 0) { + pw.println(" No packages"); + return; + } + final List<String> packageNames = new ArrayList<>(mAppsMap.keySet()); + Collections.sort(packageNames); + for (String packageName : packageNames) { + pw.println(" " + packageName + ":"); + final SparseArray<SaturationController> appUserIdMap = mAppsMap.get(packageName); + for (int i = 0; i < appUserIdMap.size(); i++) { + pw.println(" " + appUserIdMap.keyAt(i) + ":"); + appUserIdMap.valueAt(i).dump(pw); + } + } + } + } + + /** + * Retrieve the SaturationController for a given package and userId, creating all intermediate + * connections as needed. + */ + private SaturationController getSaturationControllerLocked(String packageName, + @UserIdInt int userId) { + return getOrCreateSaturationControllerLocked(getOrCreateUserIdMapLocked(packageName), + userId); + } + + /** + * Retrieve or create the mapping between the app's given package name and its userIds (and + * their SaturationControllers). + */ + private SparseArray<SaturationController> getOrCreateUserIdMapLocked(String packageName) { + if (mAppsMap.get(packageName) != null) { + return mAppsMap.get(packageName); + } + + final SparseArray<SaturationController> appUserIdMap = new SparseArray<>(); + mAppsMap.put(packageName, appUserIdMap); + return appUserIdMap; + } + + /** + * Retrieve or create the mapping between an app's given userId and SaturationController. + */ + private SaturationController getOrCreateSaturationControllerLocked( + SparseArray<SaturationController> appUserIdMap, @UserIdInt int userId) { + if (appUserIdMap.get(userId) != null) { + return appUserIdMap.get(userId); + } + + final SaturationController saturationController = new SaturationController(); + appUserIdMap.put(userId, saturationController); + return saturationController; + } + + @VisibleForTesting + static void computeGrayscaleTransformMatrix(float saturation, float[] matrix) { + float desaturation = 1.0f - saturation; + float[] luminance = {0.231f * desaturation, 0.715f * desaturation, + 0.072f * desaturation}; + matrix[0] = luminance[0] + saturation; + matrix[1] = luminance[0]; + matrix[2] = luminance[0]; + matrix[3] = luminance[1]; + matrix[4] = luminance[1] + saturation; + matrix[5] = luminance[1]; + matrix[6] = luminance[2]; + matrix[7] = luminance[2]; + matrix[8] = luminance[2] + saturation; + } + + private static class SaturationController { + + private final List<WeakReference<ColorTransformController>> mControllerRefs = + new ArrayList<>(); + private int mSaturationLevel = 100; + private float[] mTransformMatrix = new float[9]; + + private boolean setSaturationLevel(int saturationLevel) { + mSaturationLevel = saturationLevel; + if (!mControllerRefs.isEmpty()) { + return updateState(); + } + return false; + } + + private boolean addColorTransformController( + WeakReference<ColorTransformController> controller) { + mControllerRefs.add(controller); + if (mSaturationLevel != 100) { + return updateState(); + } else { + clearExpiredReferences(); + } + return false; + } + + private boolean updateState() { + computeGrayscaleTransformMatrix(mSaturationLevel / 100f, mTransformMatrix); + + boolean updated = false; + final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs + .iterator(); + while (iterator.hasNext()) { + WeakReference<ColorTransformController> controllerRef = iterator.next(); + final ColorTransformController controller = controllerRef.get(); + if (controller != null) { + controller.applyAppSaturation(mTransformMatrix, TRANSLATION_VECTOR); + updated = true; + } else { + // Purge cleared refs lazily to avoid accumulating a lot of dead windows + iterator.remove(); + } + } + return updated; + + } + + private void clearExpiredReferences() { + final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs + .iterator(); + while (iterator.hasNext()) { + WeakReference<ColorTransformController> controllerRef = iterator.next(); + final ColorTransformController controller = controllerRef.get(); + if (controller == null) { + iterator.remove(); + } + } + } + + private void dump(PrintWriter pw) { + pw.println(" mSaturationLevel: " + mSaturationLevel); + pw.println(" mControllerRefs count: " + mControllerRefs.size()); + } + } +} diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index 2fe17d8f3c98..9223739406ee 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -27,6 +27,7 @@ import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Size; import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -36,8 +37,8 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; -import android.hardware.display.ColorDisplayManager; import android.graphics.ColorSpace; +import android.hardware.display.ColorDisplayManager; import android.hardware.display.IColorDisplayManager; import android.net.Uri; import android.opengl.Matrix; @@ -50,18 +51,22 @@ import android.provider.Settings.Secure; import android.provider.Settings.System; import android.util.MathUtils; import android.util.Slog; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AnimationUtils; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ColorDisplayController; +import com.android.internal.util.DumpUtils; import com.android.server.DisplayThread; import com.android.server.SystemService; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; +import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; @@ -398,8 +403,33 @@ public final class ColorDisplayService extends SystemService { } }; + /** + * Matrix and offset used for converting color to grayscale. + */ + private static final float[] MATRIX_GRAYSCALE = new float[]{ + .2126f, .2126f, .2126f, 0f, + .7152f, .7152f, .7152f, 0f, + .0722f, .0722f, .0722f, 0f, + 0f, 0f, 0f, 1f + }; + + /** + * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color + * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and + * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's + * ProgramCache for full implementation details. + */ + private static final float[] MATRIX_INVERT_COLOR = new float[] { + 0.402f, -0.598f, -0.599f, 0f, + -1.174f, -0.174f, -1.175f, 0f, + -0.228f, -0.228f, 0.772f, 0f, + 1f, 1f, 1f, 1f + }; + private final Handler mHandler; + private final AppSaturationController mAppSaturationController = new AppSaturationController(); + private int mCurrentUser = UserHandle.USER_NULL; private ContentObserver mUserSetupObserver; private boolean mBootCompleted; @@ -537,9 +567,16 @@ public final class ColorDisplayService extends SystemService { case System.DISPLAY_COLOR_MODE: onDisplayColorModeChanged(mNightDisplayController.getColorMode()); break; - case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED: case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED: - onAccessibilityTransformChanged(); + onAccessibilityInversionChanged(); + onAccessibilityActivated(); + break; + case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED: + onAccessibilityDaltonizerChanged(); + onAccessibilityActivated(); + break; + case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER: + onAccessibilityDaltonizerChanged(); break; case Secure.DISPLAY_WHITE_BALANCE_ENABLED: updateDisplayWhiteBalanceStatus(); @@ -568,6 +605,9 @@ public final class ColorDisplayService extends SystemService { cr.registerContentObserver( Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED), false /* notifyForDescendants */, mContentObserver, mCurrentUser); + cr.registerContentObserver( + Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER), + false /* notifyForDescendants */, mContentObserver, mCurrentUser); cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED), false /* notifyForDescendants */, mContentObserver, mCurrentUser); @@ -698,10 +738,43 @@ public final class ColorDisplayService extends SystemService { dtm.setColorMode(mode, mNightDisplayTintController.getMatrix()); } - private void onAccessibilityTransformChanged() { + private void onAccessibilityActivated() { onDisplayColorModeChanged(mNightDisplayController.getColorMode()); } + /** + * Apply the accessibility daltonizer transform based on the settings value. + */ + private void onAccessibilityDaltonizerChanged() { + final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(), + Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0; + final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(), + Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, + AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser) + : AccessibilityManager.DALTONIZER_DISABLED; + + final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); + if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) { + // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale. + dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, + MATRIX_GRAYSCALE); + dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED); + } else { + dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null); + dtm.setDaltonizerMode(daltonizerMode); + } + } + + /** + * Apply the accessibility inversion transform based on the settings value. + */ + private void onAccessibilityInversionChanged() { + final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(), + Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0; + final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); + dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR, + enabled ? MATRIX_INVERT_COLOR : null); + } /** * Applies current color temperature matrix, or removes it if deactivated. @@ -829,6 +902,22 @@ public final class ColorDisplayService extends SystemService { return LocalDateTime.MIN; } + private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) { + return mAppSaturationController + .setSaturationLevel(packageName, mCurrentUser, saturationLevel); + } + + private void dumpInternal(PrintWriter pw) { + pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)"); + pw.println("Night Display:"); + if (ColorDisplayManager.isNightDisplayAvailable(getContext())) { + pw.println(" Activated: " + mNightDisplayTintController.isActivated()); + } else { + pw.println(" Not available"); + } + mAppSaturationController.dump(pw); + } + private abstract class NightDisplayAutoMode { public abstract void onActivated(boolean activated); @@ -1132,6 +1221,16 @@ public final class ColorDisplayService extends SystemService { public void dump(PrintWriter pw) { mDisplayWhiteBalanceTintController.dump(pw); } + + /** + * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and + * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed. + */ + public boolean attachColorTransformController(String packageName, int uid, + WeakReference<ColorTransformController> controller) { + return mAppSaturationController + .addColorTransformController(packageName, uid, controller); + } } /** @@ -1163,6 +1262,15 @@ public final class ColorDisplayService extends SystemService { } } + /** + * Interface for applying transforms to a given AppWindow. + */ + public interface ColorTransformController { + + /** Apply the given saturation (grayscale) matrix to the associated AppWindow. */ + void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation); + } + private final class BinderService extends IColorDisplayManager.Stub { @Override @@ -1196,5 +1304,30 @@ public final class ColorDisplayService extends SystemService { } return true; } + + @Override + public boolean setAppSaturationLevel(String packageName, int level) { + getContext().enforceCallingPermission( + Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, + "Permission required to set display saturation level"); + final long token = Binder.clearCallingIdentity(); + try { + return setAppSaturationLevelInternal(packageName, level); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; + + final long token = Binder.clearCallingIdentity(); + try { + dumpInternal(pw); + } finally { + Binder.restoreCallingIdentity(token); + } + } } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 979de66f1dc8..669ff2b7bad5 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -138,6 +138,9 @@ public class InputManagerService extends IInputManager.Stub private final Context mContext; private final InputManagerHandler mHandler; + // Context cache used for loading pointer resources. + private Context mDisplayContext; + private final File mDoubleTouchGestureEnableFile; private WindowManagerCallbacks mWindowManagerCallbacks; @@ -1923,8 +1926,25 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private PointerIcon getPointerIcon() { - return PointerIcon.getDefaultIcon(mContext); + private PointerIcon getPointerIcon(int displayId) { + return PointerIcon.getDefaultIcon(getContextForDisplay(displayId)); + } + + private Context getContextForDisplay(int displayId) { + if (mDisplayContext != null && mDisplayContext.getDisplay().getDisplayId() == displayId) { + return mDisplayContext; + } + + if (mContext.getDisplay().getDisplayId() == displayId) { + mDisplayContext = mContext; + return mDisplayContext; + } + + // Create and cache context for non-default display. + final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + final Display display = displayManager.getDisplay(displayId); + mDisplayContext = mContext.createDisplayContext(display); + return mDisplayContext; } // Native callback. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 7ff6a2fc5574..7788d268f5c9 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2903,7 +2903,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext, @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) { - final int userId = UserHandle.getUserId(Binder.getCallingUid()); + final int callingUserId = UserHandle.getCallingUserId(); + final int userId; + if (PER_PROFILE_IME_ENABLED && attribute != null && attribute.targetInputMethodUser != null + && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { + mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Using EditorInfo.user requires INTERACT_ACROSS_USERS_FULL."); + userId = attribute.targetInputMethodUser.getIdentifier(); + if (!mUserManagerInternal.isUserRunning(userId)) { + // There is a chance that we hit here because of race condition. Let's just return + // an error code instead of crashing the caller process, which at least has + // INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important process. + Slog.e(TAG, "User #" + userId + " is not running."); + return InputBindResult.INVALID_USER; + } + } else { + userId = callingUserId; + } InputBindResult res = null; synchronized (mMethodMap) { // Needs to check the validity before clearing calling identity diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java index 827c0f1df71a..4d9b5f5d01c6 100644 --- a/services/core/java/com/android/server/job/JobConcurrencyManager.java +++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java @@ -18,16 +18,34 @@ package com.android.server.job; import android.app.ActivityManager; import android.app.job.JobInfo; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.PowerManager; import android.os.RemoteException; import android.util.Slog; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.StatLogger; +import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.StateController; import java.util.Iterator; import java.util.List; +/** + * This class decides, given the various configuration and the system status, how many more jobs + * can start. + */ class JobConcurrencyManager { private static final String TAG = JobSchedulerService.TAG; private static final boolean DEBUG = JobSchedulerService.DEBUG; @@ -35,6 +53,16 @@ class JobConcurrencyManager { private final Object mLock; private final JobSchedulerService mService; private final JobSchedulerService.Constants mConstants; + private final Context mContext; + private final Handler mHandler; + + private PowerManager mPowerManager; + + private boolean mCurrentInteractiveState; + private boolean mEffectiveInteractiveState; + + private long mLastScreenOnRealtime; + private long mLastScreenOffRealtime; private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; @@ -50,10 +78,193 @@ class JobConcurrencyManager { int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; + /** Max job counts according to the current system state. */ + private JobSchedulerService.MaxJobCounts mMaxJobCounts; + + private final JobCountTracker mJobCountTracker = new JobCountTracker(); + + /** Current memory trim level. */ + private int mLastMemoryTrimLevel; + + /** Used to throttle heavy API calls. */ + private long mNextSystemStateRefreshTime; + private static final int SYSTEM_STATE_REFRESH_MIN_INTERVAL = 1000; + + private final StatLogger mStatLogger = new StatLogger(new String[]{ + "assignJobsToContexts", + "refreshSystemState", + }); + + interface Stats { + int ASSIGN_JOBS_TO_CONTEXTS = 0; + int REFRESH_SYSTEM_STATE = 1; + + int COUNT = REFRESH_SYSTEM_STATE + 1; + } + JobConcurrencyManager(JobSchedulerService service) { mService = service; mLock = mService.mLock; mConstants = service.mConstants; + mContext = service.getContext(); + + mHandler = BackgroundThread.getHandler(); + } + + public void onSystemReady() { + mPowerManager = mContext.getSystemService(PowerManager.class); + + final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(mReceiver, filter); + + onInteractiveStateChanged(mPowerManager.isInteractive()); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case Intent.ACTION_SCREEN_ON: + onInteractiveStateChanged(true); + break; + case Intent.ACTION_SCREEN_OFF: + onInteractiveStateChanged(false); + break; + } + } + }; + + /** + * Called when the screen turns on / off. + */ + private void onInteractiveStateChanged(boolean interactive) { + synchronized (mLock) { + if (mCurrentInteractiveState == interactive) { + return; + } + mCurrentInteractiveState = interactive; + if (DEBUG) { + Slog.d(TAG, "Interactive: " + interactive); + } + + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + if (interactive) { + mLastScreenOnRealtime = now; + mEffectiveInteractiveState = true; + + mHandler.removeCallbacks(mRampUpForScreenOff); + } else { + mLastScreenOffRealtime = now; + + // Set mEffectiveInteractiveState to false after the delay, when we may increase + // the concurrency. + // We don't need a wakeup alarm here. When there's a pending job, there should + // also be jobs running too, meaning the device should be awake. + + // Note: we can't directly do postDelayed(this::rampUpForScreenOn), because + // we need the exact same instance for removeCallbacks(). + mHandler.postDelayed(mRampUpForScreenOff, + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue()); + } + } + } + + private final Runnable mRampUpForScreenOff = this::rampUpForScreenOff; + + /** + * Called in {@link Constants#SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS} after + * the screen turns off, in order to increase concurrency. + */ + private void rampUpForScreenOff() { + synchronized (mLock) { + // Make sure the screen has really been off for the configured duration. + // (There could be a race.) + if (!mEffectiveInteractiveState) { + return; + } + if (mLastScreenOnRealtime > mLastScreenOffRealtime) { + return; + } + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + if ((mLastScreenOffRealtime + + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue()) + > now) { + return; + } + + mEffectiveInteractiveState = false; + + if (DEBUG) { + Slog.d(TAG, "Ramping up concurrency"); + } + + mService.maybeRunPendingJobsLocked(); + } + } + + private boolean isFgJob(JobStatus job) { + return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP; + } + + @GuardedBy("mLock") + private void refreshSystemStateLocked() { + final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis(); + + // Only refresh the information every so often. + if (nowUptime < mNextSystemStateRefreshTime) { + return; + } + + final long start = mStatLogger.getTime(); + mNextSystemStateRefreshTime = nowUptime + SYSTEM_STATE_REFRESH_MIN_INTERVAL; + + mLastMemoryTrimLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + try { + mLastMemoryTrimLevel = ActivityManager.getService().getMemoryTrimLevel(); + } catch (RemoteException e) { + } + + mStatLogger.logDurationStat(Stats.REFRESH_SYSTEM_STATE, start); + } + + @GuardedBy("mLock") + private void updateMaxCountsLocked() { + refreshSystemStateLocked(); + + if (mEffectiveInteractiveState) { + // Screen on + switch (mLastMemoryTrimLevel) { + case ProcessStats.ADJ_MEM_FACTOR_MODERATE: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_MODERATE; + break; + case ProcessStats.ADJ_MEM_FACTOR_LOW: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_LOW; + break; + case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_CRITICAL; + break; + default: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_NORMAL; + break; + } + } else { + // Screen off + switch (mLastMemoryTrimLevel) { + case ProcessStats.ADJ_MEM_FACTOR_MODERATE: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_MODERATE; + break; + case ProcessStats.ADJ_MEM_FACTOR_LOW: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_LOW; + break; + case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_CRITICAL; + break; + default: + mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_NORMAL; + break; + } + } } /** @@ -62,7 +273,17 @@ class JobConcurrencyManager { * run higher priority ones. * Lock on mJobs before calling this function. */ + @GuardedBy("mLock") void assignJobsToContextsLocked() { + final long start = mStatLogger.getTime(); + + assignJobsToContextsInternalLocked(); + + mStatLogger.logDurationStat(Stats.ASSIGN_JOBS_TO_CONTEXTS, start); + } + + @GuardedBy("mLock") + private void assignJobsToContextsInternalLocked() { if (DEBUG) { Slog.d(TAG, printPendingQueueLocked()); } @@ -72,60 +293,63 @@ class JobConcurrencyManager { final List<JobServiceContext> activeServices = mService.mActiveServices; final List<StateController> controllers = mService.mControllers; - int memLevel; - try { - memLevel = ActivityManager.getService().getMemoryTrimLevel(); - } catch (RemoteException e) { - memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; - } - switch (memLevel) { - case ProcessStats.ADJ_MEM_FACTOR_MODERATE: - mService.mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT; - break; - case ProcessStats.ADJ_MEM_FACTOR_LOW: - mService.mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT; - break; - case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: - mService.mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT; - break; - default: - mService.mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT; - break; - } + updateMaxCountsLocked(); // To avoid GC churn, we recycle the arrays. JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap; boolean[] slotChanged = mRecycledSlotChanged; int[] preferredUidForContext = mRecycledPreferredUidForContext; - int numTotalRunningJobs = 0; - int numForegroundJobs = 0; + + // Initialize the work variables and also count running jobs. + mJobCountTracker.reset( + mMaxJobCounts.getTotalMax(), + mMaxJobCounts.getMaxBg(), + mMaxJobCounts.getMinBg()); + for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { final JobServiceContext js = mService.mActiveServices.get(i); final JobStatus status = js.getRunningJobLocked(); + if ((contextIdToJobMap[i] = status) != null) { - numTotalRunningJobs++; - if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { - numForegroundJobs++; - } + mJobCountTracker.incrementRunningJobCount(isFgJob(status)); } + slotChanged[i] = false; preferredUidForContext[i] = js.getPreferredUid(); } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); } - for (int i=0; i<pendingJobs.size(); i++) { - final JobStatus nextPending = pendingJobs.get(i); + + // Next, update the job priorities, and also count the pending FG / BG jobs. + for (int i = 0; i < pendingJobs.size(); i++) { + final JobStatus pending = pendingJobs.get(i); // If job is already running, go to next job. + int jobRunningContext = findJobContextIdFromMap(pending, contextIdToJobMap); + if (jobRunningContext != -1) { + continue; + } + + final int priority = mService.evaluateJobPriorityLocked(pending); + pending.lastEvaluatedPriority = priority; + + mJobCountTracker.incrementPendingJobCount(isFgJob(pending)); + } + + mJobCountTracker.onCountDone(); + + for (int i = 0; i < pendingJobs.size(); i++) { + final JobStatus nextPending = pendingJobs.get(i); + + // Unfortunately we need to repeat this relatively expensive check. int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); if (jobRunningContext != -1) { continue; } - final int priority = mService.evaluateJobPriorityLocked(nextPending); - nextPending.lastEvaluatedPriority = priority; + final boolean isPendingFg = isFgJob(nextPending); // Find an available slot for nextPending. The context should be available OR // it should have lowest priority among all running jobs @@ -137,18 +361,10 @@ class JobConcurrencyManager { JobStatus job = contextIdToJobMap[j]; int preferredUid = preferredUidForContext[j]; if (job == null) { - final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs; - final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP) - && (numForegroundJobs < mConstants.FG_JOB_COUNT); final boolean preferredUidOkay = (preferredUid == nextPending.getUid()) || (preferredUid == JobServiceContext.NO_PREFERRED_UID); - // TODO: The following check is slightly wrong. - // Depending on how the pending jobs are sorted, we sometimes cap the total - // job count at mMaxActiveJobs (when all jobs are FG jobs), or - // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs - // and then FG_JOB_COUNT FG jobs.) - if ((totalCountOk || fgCountOk) && preferredUidOkay) { + if (preferredUidOkay && mJobCountTracker.canJobStart(isPendingFg)) { // This slot is free, and we haven't yet hit the limit on // concurrent jobs... we can just throw the job in to here. selectedContextId = j; @@ -183,16 +399,18 @@ class JobConcurrencyManager { } if (startingJob) { // Increase the counters when we're going to start a job. - numTotalRunningJobs++; - if (priority >= JobInfo.PRIORITY_TOP_APP) { - numForegroundJobs++; - } + mJobCountTracker.onStartingNewJob(isPendingFg); } } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); } - tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs); + + mJobCountTracker.logStatus(); + + tracker.noteConcurrency(mJobCountTracker.getTotalRunningJobCountToNote(), + mJobCountTracker.getFgRunningJobCountToNote()); + for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { boolean preservePreferredUid = false; if (slotChanged[i]) { @@ -228,7 +446,7 @@ class JobConcurrencyManager { } } - int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { + private static int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { for (int i=0; i<map.length; i++) { if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) { return i; @@ -237,6 +455,7 @@ class JobConcurrencyManager { return -1; } + @GuardedBy("mLock") private String printPendingQueueLocked() { StringBuilder s = new StringBuilder("Pending queue: "); Iterator<JobStatus> it = mService.mPendingJobs.iterator(); @@ -251,7 +470,7 @@ class JobConcurrencyManager { return s.toString(); } - private String printContextIdToJobMap(JobStatus[] map, String initial) { + private static String printContextIdToJobMap(JobStatus[] map, String initial) { StringBuilder s = new StringBuilder(initial + ": "); for (int i=0; i<map.length; i++) { s.append("(") @@ -262,4 +481,222 @@ class JobConcurrencyManager { return s.toString(); } + + public void dumpLocked(IndentingPrintWriter pw) { + final long now = System.currentTimeMillis(); + final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis(); + + pw.println("Concurrency:"); + + pw.increaseIndent(); + try { + pw.print("Screen state: current "); + pw.print(mCurrentInteractiveState ? "ON" : "OFF"); + pw.print(" effective "); + pw.print(mEffectiveInteractiveState ? "ON" : "OFF"); + pw.println(); + + pw.print("Last screen ON : "); + TimeUtils.dumpTimeWithDelta(pw, now - nowRealtime + mLastScreenOnRealtime, now); + pw.println(); + + pw.print("Last screen OFF: "); + TimeUtils.dumpTimeWithDelta(pw, now - nowRealtime + mLastScreenOffRealtime, now); + pw.println(); + + pw.println(); + + pw.println("Current max jobs:"); + pw.println(" "); + pw.println(mJobCountTracker); + + pw.println(); + + pw.print("mLastMemoryTrimLevel: "); + pw.print(mLastMemoryTrimLevel); + pw.println(); + + mStatLogger.dump(pw); + } finally { + pw.decreaseIndent(); + } + } + + public void dumpProtoLocked(ProtoOutputStream proto) { + // TODO Implement it. + } + + /** + * This class decides, taking into account {@link #mMaxJobCounts} and how many jos are running / + * pending, how many more job can start. + * + * Extracted for testing and logging. + */ + @VisibleForTesting + static class JobCountTracker { + private int mConfigNumTotalMaxJobs; + private int mConfigNumMaxBgJobs; + private int mConfigNumMinBgJobs; + + private int mNumRunningFgJobs; + private int mNumRunningBgJobs; + + private int mNumPendingFgJobs; + private int mNumPendingBgJobs; + + private int mNumStartingFgJobs; + private int mNumStartingBgJobs; + + private int mNumReservedForBg; + private int mNumActualMaxFgJobs; + private int mNumActualMaxBgJobs; + + void reset(int numTotalMaxJobs, int numMaxBgJobs, int numMinBgJobs) { + mConfigNumTotalMaxJobs = numTotalMaxJobs; + mConfigNumMaxBgJobs = numMaxBgJobs; + mConfigNumMinBgJobs = numMinBgJobs; + + mNumRunningFgJobs = 0; + mNumRunningBgJobs = 0; + + mNumPendingFgJobs = 0; + mNumPendingBgJobs = 0; + + mNumStartingFgJobs = 0; + mNumStartingBgJobs = 0; + + mNumReservedForBg = 0; + mNumActualMaxFgJobs = 0; + mNumActualMaxBgJobs = 0; + } + + void incrementRunningJobCount(boolean isFg) { + if (isFg) { + mNumRunningFgJobs++; + } else { + mNumRunningBgJobs++; + } + } + + void incrementPendingJobCount(boolean isFg) { + if (isFg) { + mNumPendingFgJobs++; + } else { + mNumPendingBgJobs++; + } + } + + void onStartingNewJob(boolean isFg) { + if (isFg) { + mNumStartingFgJobs++; + } else { + mNumStartingBgJobs++; + } + } + + void onCountDone() { + // Note some variables are used only here but are made class members in order to have + // them on logcat / dumpsys. + + // How many slots should we allocate to BG jobs at least? + // That's basically "getMinBg()", but if there are less jobs, decrease it. + // (e.g. even if min-bg is 2, if there's only 1 running+pending job, this has to be 1.) + final int reservedForBg = Math.min( + mConfigNumMinBgJobs, + mNumRunningBgJobs + mNumPendingBgJobs); + + // However, if there are FG jobs already running, we have to adjust it. + mNumReservedForBg = Math.min(reservedForBg, + mConfigNumTotalMaxJobs - mNumRunningFgJobs); + + // Max FG is [total - [number needed for BG jobs]] + // [number needed for BG jobs] is the bigger one of [running BG] or [reserved BG] + final int maxFg = + mConfigNumTotalMaxJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg); + + // The above maxFg is the theoretical max. If there are less FG jobs, the actual + // max FG will be lower accordingly. + mNumActualMaxFgJobs = Math.min( + maxFg, + mNumRunningFgJobs + mNumPendingFgJobs); + + // Max BG is [total - actual max FG], but cap at [config max BG]. + final int maxBg = Math.min( + mConfigNumMaxBgJobs, + mConfigNumTotalMaxJobs - mNumActualMaxFgJobs); + + // If there are less BG jobs than maxBg, then reduce the actual max BG accordingly. + // This isn't needed for the logic to work, but this will give consistent output + // on logcat and dumpsys. + mNumActualMaxBgJobs = Math.min( + maxBg, + mNumRunningBgJobs + mNumPendingBgJobs); + } + + boolean canJobStart(boolean isFg) { + if (isFg) { + return mNumRunningFgJobs + mNumStartingFgJobs < mNumActualMaxFgJobs; + } else { + return mNumRunningBgJobs + mNumStartingBgJobs < mNumActualMaxBgJobs; + } + } + + public int getNumStartingFgJobs() { + return mNumStartingFgJobs; + } + + public int getNumStartingBgJobs() { + return mNumStartingBgJobs; + } + + int getTotalRunningJobCountToNote() { + return mNumRunningFgJobs + mNumRunningBgJobs + + mNumStartingFgJobs + mNumStartingBgJobs; + } + + int getFgRunningJobCountToNote() { + return mNumRunningFgJobs + mNumStartingFgJobs; + } + + void logStatus() { + if (DEBUG) { + Slog.d(TAG, "assignJobsToContexts: " + this); + } + } + + public String toString() { + final int totalFg = mNumRunningFgJobs + mNumStartingFgJobs; + final int totalBg = mNumRunningBgJobs + mNumStartingBgJobs; + return String.format( + "Config={tot=%d bg min/max=%d/%d}" + + " Running: %d / %d (%d)" + + " Pending: %d / %d (%d)" + + " Actual max: %d%s / %d%s (%d%s)" + + " Starting: %d / %d (%d)" + + " Total: %d%s / %d%s (%d%s)", + mConfigNumTotalMaxJobs, + mConfigNumMinBgJobs, + mConfigNumMaxBgJobs, + + mNumRunningFgJobs, mNumRunningBgJobs, + mNumRunningFgJobs + mNumRunningBgJobs, + + mNumPendingFgJobs, mNumPendingBgJobs, + mNumPendingFgJobs + mNumPendingBgJobs, + + mNumActualMaxFgJobs, (totalFg <= mConfigNumTotalMaxJobs) ? "" : "*", + mNumActualMaxBgJobs, (totalBg <= mConfigNumMaxBgJobs) ? "" : "*", + + mNumActualMaxFgJobs + mNumActualMaxBgJobs, + (mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumTotalMaxJobs) + ? "" : "*", + + mNumStartingFgJobs, mNumStartingBgJobs, mNumStartingFgJobs + mNumStartingBgJobs, + + totalFg, (totalFg <= mNumActualMaxFgJobs) ? "" : "*", + totalBg, (totalBg <= mNumActualMaxBgJobs) ? "" : "*", + totalFg + totalBg, (totalFg + totalBg <= mConfigNumTotalMaxJobs) ? "" : "*" + ); + } + } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 2464ca782b6e..cc0ac9a7e0a9 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -224,12 +224,6 @@ public class JobSchedulerService extends com.android.server.SystemService volatile boolean mInParole; /** - * Current limit on the number of concurrent JobServiceContext entries we want to - * keep actively running a job. - */ - int mMaxActiveJobs = 1; - - /** * A mapping of which uids are currently in the foreground to their effective priority. */ final SparseIntArray mUidPriorityOverride = new SparseIntArray(); @@ -332,41 +326,68 @@ public class JobSchedulerService extends com.android.server.SystemService } } - private static class MaxJobCounts { + static class MaxJobCounts { private final KeyValueListParser.IntValue mTotal; - private final KeyValueListParser.IntValue mBg; + private final KeyValueListParser.IntValue mMaxBg; + private final KeyValueListParser.IntValue mMinBg; - private MaxJobCounts(int totalDefault, String totalKey, int bgDefault, String bgKey) { + MaxJobCounts(int totalDefault, String totalKey, + int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) { mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault); - mBg = new KeyValueListParser.IntValue(bgKey, bgDefault); + mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault); + mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault); } public void parse(KeyValueListParser parser) { mTotal.parse(parser); - mBg.parse(parser); + mMaxBg.parse(parser); + mMinBg.parse(parser); - if (mBg.getValue() > mTotal.getValue()) { - mBg.setValue(mTotal.getValue()); + if (mTotal.getValue() < 1) { + mTotal.setValue(1); + } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) { + mTotal.setValue(MAX_JOB_CONTEXTS_COUNT); } + if (mMaxBg.getValue() < 1) { + mMaxBg.setValue(1); + } else if (mMaxBg.getValue() > mTotal.getValue()) { + mMaxBg.setValue(mTotal.getValue()); + } + if (mMinBg.getValue() < 0) { + mMinBg.setValue(0); + } else { + if (mMinBg.getValue() > mMaxBg.getValue()) { + mMinBg.setValue(mMaxBg.getValue()); + } + if (mMinBg.getValue() >= mTotal.getValue()) { + mMinBg.setValue(mTotal.getValue() - 1); + } + } } public int getTotalMax() { return mTotal.getValue(); } - public int getBgMax() { - return mBg.getValue(); + public int getMaxBg() { + return mMaxBg.getValue(); + } + + public int getMinBg() { + return mMinBg.getValue(); } public void dump(PrintWriter pw, String prefix) { mTotal.dump(pw, prefix); - mBg.dump(pw, prefix); + mMaxBg.dump(pw, prefix); + mMinBg.dump(pw, prefix); } public void dumpProto(ProtoOutputStream proto, long tagTotal, long tagBg) { mTotal.dumpProto(proto, tagTotal); - mBg.dumpProto(proto, tagBg); + mMaxBg.dumpProto(proto, tagBg); + mMinBg.dumpProto(proto, tagBg); } } @@ -386,11 +407,14 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor"; private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor"; - private static final String KEY_FG_JOB_COUNT = "fg_job_count"; - private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count"; - private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count"; - private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count"; - private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count"; + + // The following values used to be used on P and below. Do not reuse them. + private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count"; + private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count"; + private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count"; + private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count"; + private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count"; + private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT = "max_standard_reschedule_count"; private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count"; @@ -429,11 +453,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1; private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; private static final float DEFAULT_MODERATE_USE_FACTOR = .5f; - private static final int DEFAULT_FG_JOB_COUNT = 4; - private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6; - private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4; - private static final int DEFAULT_BG_LOW_JOB_COUNT = 1; - private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1; private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE; private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE; private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; @@ -506,66 +525,52 @@ public class JobSchedulerService extends com.android.server.SystemService * This is the job execution factor that is considered to be moderate use of the system. */ float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; - /** - * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. - */ - int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT; - /** - * The maximum number of background jobs we allow when the system is in a normal - * memory state. - */ - int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT; - /** - * The maximum number of background jobs we allow when the system is in a moderate - * memory state. - */ - int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT; - /** - * The maximum number of background jobs we allow when the system is in a low - * memory state. - */ - int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT; - /** - * The maximum number of background jobs we allow when the system is in a critical - * memory state. - */ - int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT; // Max job counts for screen on / off, for each memory trim level. - // TODO Remove the old configs such as FG_JOB_COUNT and BG_*_COUNT, once the code switches - // to the below configs. - final MaxJobCounts MAX_JOB_COUNTS_ON_NORMAL = new MaxJobCounts( - 4, "max_job_total_on_normal", - 2, "max_job_bg_on_normal"); + 8, "max_job_total_on_normal", + 6, "max_job_max_bg_on_normal", + 2, "max_job_min_bg_on_normal"); final MaxJobCounts MAX_JOB_COUNTS_ON_MODERATE = new MaxJobCounts( - 4, "max_job_total_on_moderate", - 1, "max_job_bg_on_moderate"); + 8, "max_job_total_on_moderate", + 4, "max_job_max_bg_on_moderate", + 2, "max_job_min_bg_on_moderate"); final MaxJobCounts MAX_JOB_COUNTS_ON_LOW = new MaxJobCounts( - 4, "max_job_total_on_low", - 1, "max_job_bg_on_low"); + 5, "max_job_total_on_low", + 1, "max_job_max_bg_on_low", + 1, "max_job_min_bg_on_low"); final MaxJobCounts MAX_JOB_COUNTS_ON_CRITICAL = new MaxJobCounts( - 2, "max_job_total_on_critical", - 1, "max_job_bg_on_critical"); + 5, "max_job_total_on_critical", + 1, "max_job_max_bg_on_critical", + 1, "max_job_min_bg_on_critical"); final MaxJobCounts MAX_JOB_COUNTS_OFF_NORMAL = new MaxJobCounts( - 8, "max_job_total_off_normal", - 4, "max_job_bg_off_normal"); + 10, "max_job_total_off_normal", + 6, "max_job_max_bg_off_normal", + 2, "max_job_min_bg_off_normal"); final MaxJobCounts MAX_JOB_COUNTS_OFF_MODERATE = new MaxJobCounts( - 6, "max_job_total_off_moderate", - 4, "max_job_bg_off_moderate"); + 10, "max_job_total_off_moderate", + 4, "max_job_max_bg_off_moderate", + 2, "max_job_min_bg_off_moderate"); final MaxJobCounts MAX_JOB_COUNTS_OFF_LOW = new MaxJobCounts( - 4, "max_job_total_off_low", - 1, "max_job_bg_off_low"); + 5, "max_job_total_off_low", + 1, "max_job_max_bg_off_low", + 1, "max_job_min_bg_off_low"); final MaxJobCounts MAX_JOB_COUNTS_OFF_CRITICAL = new MaxJobCounts( - 2, "max_job_total_off_critical", - 1, "max_job_bg_off_critical"); + 5, "max_job_total_off_critical", + 1, "max_job_max_bg_off_critical", + 1, "max_job_min_bg_off_critical"); + + /** Wait for this long after screen off before increasing the job concurrency. */ + final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = + new KeyValueListParser.IntValue( + "screen_off_job_concurrency_increase_delay_ms", 30_000); /** * The maximum number of times we allow a job to have itself rescheduled before @@ -706,28 +711,6 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_HEAVY_USE_FACTOR); MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR, DEFAULT_MODERATE_USE_FACTOR); - FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT, - DEFAULT_FG_JOB_COUNT); - BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT, - DEFAULT_BG_NORMAL_JOB_COUNT); - if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT, - DEFAULT_BG_MODERATE_JOB_COUNT); - if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT, - DEFAULT_BG_LOW_JOB_COUNT); - if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT, - DEFAULT_BG_CRITICAL_JOB_COUNT); - if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } MAX_JOB_COUNTS_ON_NORMAL.parse(mParser); MAX_JOB_COUNTS_ON_MODERATE.parse(mParser); @@ -798,11 +781,6 @@ public class JobSchedulerService extends com.android.server.SystemService pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println(); pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); - pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println(); - pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println(); - pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println(); - pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println(); - pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println(); MAX_JOB_COUNTS_ON_NORMAL.dump(pw, ""); MAX_JOB_COUNTS_ON_MODERATE.dump(pw, ""); @@ -859,11 +837,6 @@ public class JobSchedulerService extends com.android.server.SystemService proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT); proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); - proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT); - proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT); - proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT); - proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT); - proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT); // TODO Dump max job counts. @@ -1558,6 +1531,9 @@ public class JobSchedulerService extends com.android.server.SystemService } catch (RemoteException e) { // ignored; both services live in system_server } + + mConcurrencyManager.onSystemReady(); + // Remove any jobs that are not associated with any of the current users. cancelJobsForNonExistentUsers(); // Register thermal callback @@ -1685,7 +1661,7 @@ public class JobSchedulerService extends com.android.server.SystemService * Reschedules the given job based on the job's backoff policy. It doesn't make sense to * specify an override deadline on a failed job (the failed job will run even though it's not * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any - * ready job with {@link JobStatus#numFailures} > 0 will be executed. + * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed. * * @param failureToReschedule Provided job status that we will reschedule. * @return A newly instantiated JobStatus with the same constraints as the last job except @@ -2467,7 +2443,7 @@ public class JobSchedulerService extends com.android.server.SystemService * A controller can force a job into the pending queue even if it's already running, but * here is where we decide whether to actually execute it. */ - private void maybeRunPendingJobsLocked() { + void maybeRunPendingJobsLocked() { if (DEBUG) { Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); } @@ -3480,9 +3456,12 @@ public class JobSchedulerService extends com.android.server.SystemService pw.println(); pw.print("mReadyToRock="); pw.println(mReadyToRock); pw.print("mReportedActive="); pw.println(mReportedActive); - pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs); } pw.println(); + + mConcurrencyManager.dumpLocked(pw); + + pw.println(); pw.print("PersistStats: "); pw.println(mJobs.getPersistStats()); } @@ -3634,8 +3613,8 @@ public class JobSchedulerService extends com.android.server.SystemService if (filterUid == -1) { proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock); proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive); - proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs); } + mConcurrencyManager.dumpProtoLocked(proto); } proto.flush(); diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java deleted file mode 100644 index 22fabb2cdd3e..000000000000 --- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 com.android.server.location; - -import android.content.Context; -import android.hardware.location.ActivityRecognitionHardware; -import android.hardware.location.IActivityRecognitionHardwareClient; -import android.hardware.location.IActivityRecognitionHardwareWatcher; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.internal.os.BackgroundThread; -import com.android.server.ServiceWatcher; - -/** - * Proxy class to bind GmsCore to the ActivityRecognitionHardware. - * - * @hide - */ -public class ActivityRecognitionProxy { - - private static final String TAG = "ActivityRecognitionProxy"; - - /** - * Creates an instance of the proxy and binds it to the appropriate FusedProvider. - * - * @return An instance of the proxy if it could be bound, null otherwise. - */ - public static ActivityRecognitionProxy createAndBind( - Context context, - boolean activityRecognitionHardwareIsSupported, - ActivityRecognitionHardware activityRecognitionHardware, - int overlaySwitchResId, - int defaultServicePackageNameResId, - int initialPackageNameResId) { - ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy( - context, - activityRecognitionHardwareIsSupported, - activityRecognitionHardware, - overlaySwitchResId, - defaultServicePackageNameResId, - initialPackageNameResId); - - if (activityRecognitionProxy.mServiceWatcher.start()) { - return activityRecognitionProxy; - } else { - return null; - } - } - - private final ServiceWatcher mServiceWatcher; - private final boolean mIsSupported; - private final ActivityRecognitionHardware mInstance; - - private ActivityRecognitionProxy( - Context context, - boolean activityRecognitionHardwareIsSupported, - ActivityRecognitionHardware activityRecognitionHardware, - int overlaySwitchResId, - int defaultServicePackageNameResId, - int initialPackageNameResId) { - mIsSupported = activityRecognitionHardwareIsSupported; - mInstance = activityRecognitionHardware; - - mServiceWatcher = new ServiceWatcher( - context, - TAG, - "com.android.location.service.ActivityRecognitionProvider", - overlaySwitchResId, - defaultServicePackageNameResId, - initialPackageNameResId, - BackgroundThread.getHandler()) { - @Override - protected void onBind() { - runOnBinder(ActivityRecognitionProxy.this::initializeService); - } - }; - } - - private void initializeService(IBinder binder) { - try { - String descriptor = binder.getInterfaceDescriptor(); - - if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals( - descriptor)) { - IActivityRecognitionHardwareWatcher watcher = - IActivityRecognitionHardwareWatcher.Stub.asInterface(binder); - if (mInstance != null) { - watcher.onInstanceChanged(mInstance); - } - } else if (IActivityRecognitionHardwareClient.class.getCanonicalName() - .equals(descriptor)) { - IActivityRecognitionHardwareClient client = - IActivityRecognitionHardwareClient.Stub.asInterface(binder); - client.onAvailabilityChanged(mIsSupported, mInstance); - } else { - Log.e(TAG, "Invalid descriptor found on connection: " + descriptor); - } - } catch (RemoteException e) { - Log.w(TAG, e); - } - } -} diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java index ca4f7fee494a..38c7d491f706 100644 --- a/services/core/java/com/android/server/location/GeofenceProxy.java +++ b/services/core/java/com/android/server/location/GeofenceProxy.java @@ -113,8 +113,12 @@ public final class GeofenceProxy { IGeofenceHardware geofenceHardware = IGeofenceHardware.Stub.asInterface(service); try { - geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware); - geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware); + if (mGpsGeofenceHardware != null) { + geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware); + } + if (mFusedGeofenceHardware != null) { + geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware); + } mGeofenceHardware = geofenceHardware; mServiceWatcher.runOnBinder(mUpdateGeofenceHardware); diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java index 9e339430da12..72259268aa81 100644 --- a/services/core/java/com/android/server/location/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/GnssConfiguration.java @@ -23,6 +23,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.Log; +import android.util.StatsLog; import libcore.io.IoUtils; @@ -231,6 +232,8 @@ class GnssConfiguration { mEsExtensionSec = getRangeCheckedConfigEsExtensionSec(); + logConfigurations(); + final HalInterfaceVersion gnssConfigurationIfaceVersion = native_get_gnss_configuration_version(); if (gnssConfigurationIfaceVersion != null) { @@ -282,6 +285,23 @@ class GnssConfiguration { } } + private void logConfigurations() { + StatsLog.write(StatsLog.GNSS_CONFIGURATION_REPORTED, + getSuplHost(), + getSuplPort(0), + getC2KHost(), + getC2KPort(0), + getIntConfig(CONFIG_SUPL_VER, 0), + getSuplMode(0), + getSuplEs(0) == 1, + getIntConfig(CONFIG_LPP_PROFILE, 0), + getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0), + getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1, + getIntConfig(CONFIG_GPS_LOCK, 0), + getEsExtensionSec(), + mProperties.getProperty(CONFIG_NFW_PROXY_APPS)); + } + /** * Loads GNSS properties from carrier config file. */ diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java index 845aa9dfacd6..591889dde5de 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -28,6 +28,7 @@ import android.os.PowerManager; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.util.StatsLog; import java.util.Arrays; import java.util.HashMap; @@ -278,14 +279,24 @@ class GnssVisibilityControl { if (DEBUG) Log.d(TAG, nfwNotification.toString()); final String proxyAppPackageName = nfwNotification.mProxyAppPackageName; + Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get( + proxyAppPackageName); + boolean isLocationRequestAccepted = + nfwNotification.mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED; + boolean isPermissionMismatched; + if (isLocationPermissionEnabled == null) { + isPermissionMismatched = isLocationRequestAccepted; + } else { + isPermissionMismatched = (isLocationPermissionEnabled != isLocationRequestAccepted); + } + logEvent(nfwNotification, isPermissionMismatched); + 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 " @@ -317,9 +328,7 @@ class GnssVisibilityControl { 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) { + if (isPermissionMismatched) { Log.w(TAG, "Permission mismatch. Framework proxy app " + proxyAppPackageName + " location permission is set to " + isLocationPermissionEnabled + " but GNSS non-framework location access response type is " @@ -338,6 +347,19 @@ class GnssVisibilityControl { } } + private void logEvent(NfwNotification notification, boolean isPermissionMismatched) { + StatsLog.write(StatsLog.GNSS_NFW_NOTIFICATION_REPORTED, + notification.mProxyAppPackageName, + notification.mProtocolStack, + notification.mOtherProtocolStackName, + notification.mRequestor, + notification.mRequestorId, + notification.mResponseType, + notification.mInEmergencyMode, + notification.mIsCachedLocation, + isPermissionMismatched); + } + 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 diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ea8c7922d831..8734ceb614a9 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -21,10 +21,10 @@ import static android.Manifest.permission.READ_CONTACTS; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.USER_FRP; import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled; import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential; @@ -81,9 +81,9 @@ import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.security.keystore.UserNotAuthenticatedException; import android.security.keystore.recovery.KeyChainProtectionParams; +import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.RecoveryCertPath; import android.security.keystore.recovery.WrappedApplicationKey; -import android.security.keystore.recovery.KeyChainSnapshot; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.text.TextUtils; @@ -109,9 +109,9 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.locksettings.LockSettingsStorage.CredentialHash; import com.android.server.locksettings.LockSettingsStorage.PersistentData; -import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken; +import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; import com.android.server.wm.WindowManagerInternal; import libcore.util.HexEncoding; @@ -130,8 +130,8 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -2103,11 +2103,24 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public @Nullable String importKey(@NonNull String alias, byte[] keyBytes) throws RemoteException { + public @Nullable String generateKeyWithMetadata( + @NonNull String alias, @Nullable byte[] metadata) throws RemoteException { + return mRecoverableKeyStoreManager.generateKeyWithMetadata(alias, metadata); + } + + @Override + public @Nullable String importKey(@NonNull String alias, @NonNull byte[] keyBytes) + throws RemoteException { return mRecoverableKeyStoreManager.importKey(alias, keyBytes); } @Override + public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes, + @Nullable byte[] metadata) throws RemoteException { + return mRecoverableKeyStoreManager.importKeyWithMetadata(alias, keyBytes, metadata); + } + + @Override public @Nullable String getKey(@NonNull String alias) throws RemoteException { return mRecoverableKeyStoreManager.getKey(alias); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index f1951b1e31b1..1f9b027be030 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -26,6 +26,7 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Log; +import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -258,9 +259,9 @@ public class KeySyncTask implements Runnable { localLskfHash = hashCredentialsBySaltedSha256(salt, mCredential); } - Map<String, SecretKey> rawKeys; + Map<String, Pair<SecretKey, byte[]>> rawKeysWithMetadata; try { - rawKeys = getKeysToSync(recoveryAgentUid); + rawKeysWithMetadata = getKeysToSync(recoveryAgentUid); } catch (GeneralSecurityException e) { Log.e(TAG, "Failed to load recoverable keys for sync", e); return; @@ -278,7 +279,9 @@ public class KeySyncTask implements Runnable { } // Only include insecure key material for test if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(rootCertAlias)) { - rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); + rawKeysWithMetadata = + mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys( + rawKeysWithMetadata); } SecretKey recoveryKey; @@ -292,7 +295,7 @@ public class KeySyncTask implements Runnable { Map<String, byte[]> encryptedApplicationKeys; try { encryptedApplicationKeys = KeySyncUtils.encryptKeysWithRecoveryKey( - recoveryKey, rawKeys); + recoveryKey, rawKeysWithMetadata); } catch (InvalidKeyException | NoSuchAlgorithmException e) { Log.wtf(TAG, "Should be impossible: could not encrypt application keys with random key", @@ -356,7 +359,8 @@ public class KeySyncTask implements Runnable { .setCounterId(counterId) .setServerParams(vaultHandle) .setKeyChainProtectionParams(metadataList) - .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys)) + .setWrappedApplicationKeys( + createApplicationKeyEntries(encryptedApplicationKeys, rawKeysWithMetadata)) .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey); try { keyChainSnapshotBuilder.setTrustedHardwareCertPath(certPath); @@ -405,7 +409,7 @@ public class KeySyncTask implements Runnable { /** * Returns all of the recoverable keys for the user. */ - private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid) + private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid) throws InsecureUserException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException, InvalidKeyException, InvalidAlgorithmParameterException, IOException { @@ -521,12 +525,14 @@ public class KeySyncTask implements Runnable { } private static List<WrappedApplicationKey> createApplicationKeyEntries( - Map<String, byte[]> encryptedApplicationKeys) { + Map<String, byte[]> encryptedApplicationKeys, + Map<String, Pair<SecretKey, byte[]>> originalKeysWithMetadata) { ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>(); for (String alias : encryptedApplicationKeys.keySet()) { keyEntries.add(new WrappedApplicationKey.Builder() .setAlias(alias) .setEncryptedKeyMaterial(encryptedApplicationKeys.get(alias)) + .setMetadata(originalKeysWithMetadata.get(alias).second) .build()); } return keyEntries; diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java index 8e6f9cbbb260..24d575e27bdd 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java @@ -16,6 +16,9 @@ package com.android.server.locksettings.recoverablekeystore; +import android.annotation.Nullable; +import android.util.Pair; + import com.android.internal.annotations.VisibleForTesting; import java.nio.ByteBuffer; @@ -152,15 +155,28 @@ public class KeySyncUtils { * @hide */ public static Map<String, byte[]> encryptKeysWithRecoveryKey( - SecretKey recoveryKey, Map<String, SecretKey> keys) + SecretKey recoveryKey, Map<String, Pair<SecretKey, byte[]>> keys) throws NoSuchAlgorithmException, InvalidKeyException { HashMap<String, byte[]> encryptedKeys = new HashMap<>(); for (String alias : keys.keySet()) { - SecretKey key = keys.get(alias); + SecretKey key = keys.get(alias).first; + byte[] metadata = keys.get(alias).second; + byte[] header; + if (metadata == null) { + header = ENCRYPTED_APPLICATION_KEY_HEADER; + } else { + // The provided metadata, if non-empty, will be bound to the authenticated + // encryption process of the key material. As a result, the ciphertext cannot be + // decrypted if a wrong metadata is provided during the recovery/decryption process. + // Note that Android P devices do not have the API to provide the optional metadata, + // so all the keys with non-empty metadata stored on Android Q+ devices cannot be + // recovered on Android P devices. + header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, metadata); + } byte[] encryptedKey = SecureBox.encrypt( /*theirPublicKey=*/ null, /*sharedSecret=*/ recoveryKey.getEncoded(), - /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, + /*header=*/ header, /*payload=*/ key.getEncoded()); encryptedKeys.put(alias, encryptedKey); } @@ -257,12 +273,19 @@ public class KeySyncUtils { * @throws AEADBadTagException if the message has been tampered with or was encrypted with a * different key. */ - public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey) + public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey, + @Nullable byte[] applicationKeyMetadata) throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException { + byte[] header; + if (applicationKeyMetadata == null) { + header = ENCRYPTED_APPLICATION_KEY_HEADER; + } else { + header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, applicationKeyMetadata); + } return SecureBox.decrypt( /*ourPrivateKey=*/ null, /*sharedSecret=*/ recoveryKey, - /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, + /*header=*/ header, /*encryptedPayload=*/ encryptedApplicationKey); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java index c249468a80a7..1692e5cb9842 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java @@ -17,6 +17,8 @@ package com.android.server.locksettings.recoverablekeystore; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Log; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; @@ -24,7 +26,6 @@ import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.Locale; -import android.util.Log; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -84,6 +85,8 @@ public class RecoverableKeyGenerator { * @param userId The user ID of the profile to which the calling app belongs. * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in the recoverable key store. + * @param metadata The optional metadata that will be authenticated (but unencrypted) together + * with the key material when the key is uploaded to cloud. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. @@ -92,12 +95,13 @@ public class RecoverableKeyGenerator { * @hide */ public byte[] generateAndStoreKey( - PlatformEncryptionKey platformKey, int userId, int uid, String alias) + PlatformEncryptionKey platformKey, int userId, int uid, String alias, + @Nullable byte[] metadata) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { mKeyGenerator.init(KEY_SIZE_BITS); SecretKey key = mKeyGenerator.generateKey(); - WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata); long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { @@ -126,6 +130,8 @@ public class RecoverableKeyGenerator { * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in the recoverable key store. * @param keyBytes The raw bytes of the AES key to be imported. + * @param metadata The optional metadata that will be authenticated (but unencrypted) together + * with the key material when the key is uploaded to cloud. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. @@ -135,11 +141,11 @@ public class RecoverableKeyGenerator { */ public void importKey( @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias, - @NonNull byte[] keyBytes) + @NonNull byte[] keyBytes, @Nullable byte[] metadata) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM); - WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata); long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index fc5184d1438f..ed864c0221c9 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -20,8 +20,8 @@ import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CE import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED; import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE; import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER; -import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; +import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT; import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING; import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR; import static android.security.keystore.recovery.RecoveryController.ERROR_SESSION_EXPIRED; @@ -35,12 +35,12 @@ import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; +import android.security.KeyStore; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.RecoveryCertPath; import android.security.keystore.recovery.RecoveryController; import android.security.keystore.recovery.WrappedApplicationKey; -import android.security.KeyStore; import android.util.ArrayMap; import android.util.Log; @@ -59,7 +59,6 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnaps import java.io.IOException; import java.security.InvalidKeyException; -import java.security.KeyFactory; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; @@ -70,7 +69,6 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -606,7 +604,8 @@ public class RecoverableKeyStoreManager { try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); - Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); + Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, + applicationKeys); return importKeyMaterials(userId, uid, keysByAlias); } catch (KeyStoreException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); @@ -625,7 +624,8 @@ public class RecoverableKeyStoreManager { * @throws KeyStoreException if an error occurs importing the key or getting the grant. */ private @NonNull Map<String, String> importKeyMaterials( - int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException { + int userId, int uid, Map<String, byte[]> keysByAlias) + throws KeyStoreException { ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size()); for (String alias : keysByAlias.keySet()) { mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias)); @@ -674,9 +674,29 @@ public class RecoverableKeyStoreManager { * Generates a key named {@code alias} in caller's namespace. * The key is stored in system service keystore namespace. * + * @param alias the alias provided by caller as a reference to the key. * @return grant alias, which caller can use to access the key. + * @throws RemoteException if certain internal errors occur. + * + * @deprecated Use {@link #generateKeyWithMetadata(String, byte[])} instead. */ + @Deprecated public String generateKey(@NonNull String alias) throws RemoteException { + return generateKeyWithMetadata(alias, /*metadata=*/ null); + } + + /** + * Generates a key named {@code alias} with the {@code metadata} in caller's namespace. + * The key is stored in system service keystore namespace. + * + * @param alias the alias provided by caller as a reference to the key. + * @param metadata the optional metadata blob that will authenticated (but unencrypted) together + * with the key material when the key is uploaded to cloud. + * @return grant alias, which caller can use to access the key. + * @throws RemoteException if certain internal errors occur. + */ + public String generateKeyWithMetadata(@NonNull String alias, @Nullable byte[] metadata) + throws RemoteException { checkRecoverKeyStorePermission(); Preconditions.checkNotNull(alias, "alias is null"); int uid = Binder.getCallingUid(); @@ -695,8 +715,8 @@ public class RecoverableKeyStoreManager { } try { - byte[] secretKey = - mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); + byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, + uid, alias, metadata); mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { @@ -713,10 +733,30 @@ public class RecoverableKeyStoreManager { * @return grant alias, which caller can use to access the key. * @throws RemoteException if the given key is invalid or some internal errors occur. * + * @deprecated Use {{@link #importKeyWithMetadata(String, byte[], byte[])}} instead. + * * @hide */ + @Deprecated public @Nullable String importKey(@NonNull String alias, @NonNull byte[] keyBytes) throws RemoteException { + return importKeyWithMetadata(alias, keyBytes, /*metadata=*/ null); + } + + /** + * Imports a 256-bit AES-GCM key named {@code alias} with the given {@code metadata}. The key is + * stored in system service keystore namespace. + * + * @param alias the alias provided by caller as a reference to the key. + * @param keyBytes the raw bytes of the 256-bit AES key. + * @param metadata the metadata to be authenticated (but unencrypted) together with the key. + * @return grant alias, which caller can use to access the key. + * @throws RemoteException if the given key is invalid or some internal errors occur. + * + * @hide + */ + public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes, + @Nullable byte[] metadata) throws RemoteException { checkRecoverKeyStorePermission(); Preconditions.checkNotNull(alias, "alias is null"); Preconditions.checkNotNull(keyBytes, "keyBytes is null"); @@ -745,7 +785,8 @@ public class RecoverableKeyStoreManager { try { // Wrap the key by the platform key and store the wrapped key locally - mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes); + mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes, + metadata); // Import the key to Android KeyStore and get grant mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes); @@ -812,17 +853,17 @@ public class RecoverableKeyStoreManager { * @return Map from alias to raw key material. * @throws RemoteException if an error occurred decrypting the keys. */ - private @NonNull Map<String, byte[]> recoverApplicationKeys( - @NonNull byte[] recoveryKey, + private @NonNull Map<String, byte[]> recoverApplicationKeys(@NonNull byte[] recoveryKey, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>(); for (WrappedApplicationKey applicationKey : applicationKeys) { String alias = applicationKey.getAlias(); byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial(); + byte[] keyMetadata = applicationKey.getMetadata(); try { - byte[] keyMaterial = - KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); + byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, + encryptedKeyMaterial, keyMetadata); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java index 5ba3cceec6d0..ffea488b670d 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java @@ -18,17 +18,20 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; -import com.android.internal.widget.LockPatternUtils; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keystore.recovery.TrustedRootCertificates; import android.util.Log; +import android.util.Pair; + +import com.android.internal.widget.LockPatternUtils; -import java.util.HashMap; import java.security.cert.X509Certificate; +import java.util.HashMap; import java.util.Map; + import javax.crypto.SecretKey; /** @@ -84,22 +87,24 @@ public class TestOnlyInsecureCertificateHelper { || isTestOnlyCertificateAlias(rootCertificateAlias); } - public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { + boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) && (credential != null) && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX); } - public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) { + Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys( + Map<String, Pair<SecretKey, byte[]>> rawKeys) { if (rawKeys == null) { return null; } - Map<String, SecretKey> filteredKeys = new HashMap<>(); - for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) { + Map<String, Pair<SecretKey, byte[]>> filteredKeys = new HashMap<>(); + for (Map.Entry<String, Pair<SecretKey, byte[]>> entry : rawKeys.entrySet()) { String alias = entry.getKey(); if (alias != null && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) { - filteredKeys.put(entry.getKey(), entry.getValue()); + filteredKeys.put(entry.getKey(), + Pair.create(entry.getValue().first, entry.getValue().second)); Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot"); } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java index 0077242b412a..09d7541102b5 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java @@ -16,8 +16,10 @@ package com.android.server.locksettings.recoverablekeystore; +import android.annotation.Nullable; import android.security.keystore.recovery.RecoveryController; import android.util.Log; +import android.util.Pair; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -49,6 +51,7 @@ public class WrappedKey { private final int mRecoveryStatus; private final byte[] mNonce; private final byte[] mKeyMaterial; + private final byte[] mKeyMetadata; /** * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material. @@ -58,7 +61,8 @@ public class WrappedKey { * {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does * not expose its key material. */ - public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key) + public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key, + @Nullable byte[] metadata) throws InvalidKeyException, KeyStoreException { if (key.getEncoded() == null) { throw new InvalidKeyException( @@ -96,6 +100,7 @@ public class WrappedKey { return new WrappedKey( /*nonce=*/ cipher.getIV(), /*keyMaterial=*/ encryptedKeyMaterial, + /*keyMetadata=*/ metadata, /*platformKeyGenerationId=*/ wrappingKey.getGenerationId(), RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); } @@ -110,11 +115,10 @@ public class WrappedKey { * @see RecoveryController#RECOVERY_STATUS_SYNC_IN_PROGRESS * @hide */ - public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) { - mNonce = nonce; - mKeyMaterial = keyMaterial; - mPlatformKeyGenerationId = platformKeyGenerationId; - mRecoveryStatus = RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS; + public WrappedKey(byte[] nonce, byte[] keyMaterial, @Nullable byte[] keyMetadata, + int platformKeyGenerationId) { + this(nonce, keyMaterial, keyMetadata, platformKeyGenerationId, + RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); } /** @@ -122,15 +126,18 @@ public class WrappedKey { * * @param nonce The nonce with which the key material was encrypted. * @param keyMaterial The encrypted bytes of the key material. + * @param keyMetadata The metadata that will be authenticated (but unencrypted) together with + * the key material when the key is uploaded to cloud. * @param platformKeyGenerationId The generation ID of the key used to wrap this key. * @param recoveryStatus recovery status of the key. * * @hide */ - public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId, - int recoveryStatus) { + public WrappedKey(byte[] nonce, byte[] keyMaterial, @Nullable byte[] keyMetadata, + int platformKeyGenerationId, int recoveryStatus) { mNonce = nonce; mKeyMaterial = keyMaterial; + mKeyMetadata = keyMetadata; mPlatformKeyGenerationId = platformKeyGenerationId; mRecoveryStatus = recoveryStatus; } @@ -154,6 +161,15 @@ public class WrappedKey { } /** + * Returns the key metadata. + * + * @hide + */ + public @Nullable byte[] getKeyMetadata() { + return mKeyMetadata; + } + + /** * Returns the generation ID of the platform key, with which this key was wrapped. * * @hide @@ -181,12 +197,12 @@ public class WrappedKey { * * @hide */ - public static Map<String, SecretKey> unwrapKeys( + public static Map<String, Pair<SecretKey, byte[]>> unwrapKeys( PlatformDecryptionKey platformKey, Map<String, WrappedKey> wrappedKeys) throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException, InvalidKeyException, InvalidAlgorithmParameterException { - HashMap<String, SecretKey> unwrappedKeys = new HashMap<>(); + HashMap<String, Pair<SecretKey, byte[]>> unwrappedKeys = new HashMap<>(); Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM); int platformKeyGenerationId = platformKey.getGenerationId(); @@ -219,7 +235,7 @@ public class WrappedKey { e); continue; } - unwrappedKeys.put(alias, key); + unwrappedKeys.put(alias, Pair.create(key, wrappedKey.getKeyMetadata())); } return unwrappedKeys; diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java index b486834235dc..8a19d62de0b9 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java @@ -23,19 +23,18 @@ import static com.android.server.locksettings.recoverablekeystore.serialization. import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS; - -import static com.android.server.locksettings.recoverablekeystore.serialization - .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; +import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID; -import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL; +import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_METADATA; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY; +import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION; @@ -49,6 +48,9 @@ import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Base64; import android.util.Xml; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -59,9 +61,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - /** * Deserializes a {@link android.security.keystore.recovery.KeyChainSnapshot} instance from XML. */ @@ -191,6 +190,10 @@ public class KeyChainSnapshotDeserializer { builder.setEncryptedKeyMaterial(readBlobTag(parser, TAG_KEY_MATERIAL)); break; + case TAG_KEY_METADATA: + builder.setMetadata(readBlobTag(parser, TAG_KEY_METADATA)); + break; + default: throw new KeyChainSnapshotParserException(String.format( Locale.US, "Unexpected tag %s in wrappedApplicationKey", name)); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java index 0f2c2fc6c959..8f85a27d4690 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java @@ -52,6 +52,7 @@ class KeyChainSnapshotSchema { static final String TAG_APPLICATION_KEY = "applicationKey"; static final String TAG_ALIAS = "alias"; static final String TAG_KEY_MATERIAL = "keyMaterial"; + static final String TAG_KEY_METADATA = "keyMetadata"; // Statics only private KeyChainSnapshotSchema() {} diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java index 235df698a674..527e879a198b 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java @@ -24,25 +24,24 @@ import static com.android.server.locksettings.recoverablekeystore.serialization. import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS; - -import static com.android.server.locksettings.recoverablekeystore.serialization - .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID; -import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL; +import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_METADATA; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY; +import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE; +import android.annotation.Nullable; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; @@ -103,6 +102,7 @@ public class KeyChainSnapshotSerializer { XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException { writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias()); writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial()); + writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata()); } private static void writeKeyChainProtectionParams( @@ -181,8 +181,11 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, byte[] propertyValue) + XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue) throws IOException { + if (propertyValue == null) { + return; + } xmlSerializer.startTag(NAMESPACE, propertyName); xmlSerializer.text(Base64.encodeToString(propertyValue, /*flags=*/ Base64.DEFAULT)); xmlSerializer.endTag(NAMESPACE, propertyName); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java index e69f73df0184..dffaffe677ad 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java @@ -104,6 +104,12 @@ public class RecoverableKeyStoreDb { values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, LAST_SYNCED_AT_UNSYNCED); values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId()); values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, wrappedKey.getRecoveryStatus()); + byte[] keyMetadata = wrappedKey.getKeyMetadata(); + if (keyMetadata == null) { + values.putNull(KeysEntry.COLUMN_NAME_KEY_METADATA); + } else { + values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, keyMetadata); + } return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values); } @@ -119,7 +125,8 @@ public class RecoverableKeyStoreDb { KeysEntry.COLUMN_NAME_NONCE, KeysEntry.COLUMN_NAME_WRAPPED_KEY, KeysEntry.COLUMN_NAME_GENERATION_ID, - KeysEntry.COLUMN_NAME_RECOVERY_STATUS}; + KeysEntry.COLUMN_NAME_RECOVERY_STATUS, + KeysEntry.COLUMN_NAME_KEY_METADATA}; String selection = KeysEntry.COLUMN_NAME_UID + " = ? AND " + KeysEntry.COLUMN_NAME_ALIAS + " = ?"; @@ -155,7 +162,17 @@ public class RecoverableKeyStoreDb { cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_GENERATION_ID)); int recoveryStatus = cursor.getInt( cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS)); - return new WrappedKey(nonce, keyMaterial, generationId, recoveryStatus); + + // Retrieve the metadata associated with the key + byte[] keyMetadata; + int metadataIdx = cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_KEY_METADATA); + if (cursor.isNull(metadataIdx)) { + keyMetadata = null; + } else { + keyMetadata = cursor.getBlob(metadataIdx); + } + + return new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, recoveryStatus); } } @@ -252,7 +269,8 @@ public class RecoverableKeyStoreDb { KeysEntry.COLUMN_NAME_NONCE, KeysEntry.COLUMN_NAME_WRAPPED_KEY, KeysEntry.COLUMN_NAME_ALIAS, - KeysEntry.COLUMN_NAME_RECOVERY_STATUS}; + KeysEntry.COLUMN_NAME_RECOVERY_STATUS, + KeysEntry.COLUMN_NAME_KEY_METADATA}; String selection = KeysEntry.COLUMN_NAME_USER_ID + " = ? AND " + KeysEntry.COLUMN_NAME_UID + " = ? AND " @@ -283,8 +301,18 @@ public class RecoverableKeyStoreDb { cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS)); int recoveryStatus = cursor.getInt( cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS)); - keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId, - recoveryStatus)); + + // Retrieve the metadata associated with the key + byte[] keyMetadata; + int metadataIdx = cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_KEY_METADATA); + if (cursor.isNull(metadataIdx)) { + keyMetadata = null; + } else { + keyMetadata = cursor.getBlob(metadataIdx); + } + + keys.put(alias, new WrappedKey(nonce, keyMaterial, keyMetadata, + platformKeyGenerationId, recoveryStatus)); } return keys; } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java index 22e77cc79831..b58ee4bc9d74 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java @@ -67,6 +67,12 @@ class RecoverableKeyStoreDbContract { * Status of the key sync {@code RecoveryController#setRecoveryStatus} */ static final String COLUMN_NAME_RECOVERY_STATUS = "recovery_status"; + + /** + * Data blob that will be authenticated (but encrypted) together with the key when the key + * is uploaded to cloud. + */ + static final String COLUMN_NAME_KEY_METADATA = "key_metadata"; } /** diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java index 43efe9cc0cbb..b0613da35d28 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java @@ -32,7 +32,7 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKe class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { private static final String TAG = "RecoverableKeyStoreDbHp"; - static final int DATABASE_VERSION = 4; + static final int DATABASE_VERSION = 5; private static final String DATABASE_NAME = "recoverablekeystore.db"; private static final String SQL_CREATE_KEYS_ENTRY = @@ -46,6 +46,7 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER," + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER," + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER," + + KeysEntry.COLUMN_NAME_KEY_METADATA + " BLOB," + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + "," + KeysEntry.COLUMN_NAME_ALIAS + "))"; @@ -135,6 +136,11 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { oldVersion = 4; } + if (oldVersion < 5 && newVersion >= 5) { + upgradeDbForVersion5(db); + oldVersion = 5; + } + if (oldVersion != newVersion) { Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version"); } @@ -166,6 +172,13 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { /*defaultStr=*/ null); } + private void upgradeDbForVersion5(SQLiteDatabase db) { + Log.d(TAG, "Updating recoverable keystore database to version 5"); + // adds a column to store the metadata for application keys + addColumnToTable(db, KeysEntry.TABLE_NAME, + KeysEntry.COLUMN_NAME_KEY_METADATA, "BLOB", /*defaultStr=*/ null); + } + private static void addColumnToTable( SQLiteDatabase db, String tableName, String column, String columnType, String defaultStr) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7323e93ebf84..1798617a9fce 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3509,7 +3509,7 @@ public class NotificationManagerService extends SystemService { getContext().sendBroadcastAsUser(new Intent( NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(pkg) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT), UserHandle.of(userId), null); handleSavePolicyFile(); } 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/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index d6ab5f717568..65fc9824c76e 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -27,24 +27,29 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageInfo; import android.os.BatteryManager; import android.os.Environment; import android.os.ServiceManager; import android.os.SystemProperties; +import android.os.UserHandle; import android.os.storage.StorageManager; import android.util.ArraySet; import android.util.Log; +import android.util.StatsLog; -import com.android.server.pm.dex.DexManager; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PinnerService; +import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import java.io.File; +import java.nio.file.Paths; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; /** * {@hide} @@ -289,6 +294,50 @@ public class BackgroundDexOptService extends JobService { return result; } + /** + * Get the size of the directory. It uses recursion to go over all files. + * @param f + * @return + */ + private long getDirectorySize(File f) { + long size = 0; + if (f.isDirectory()) { + for (File file: f.listFiles()) { + size += getDirectorySize(file); + } + } else { + size = f.length(); + } + return size; + } + + /** + * Get the size of a package. + * @param pkg + */ + private long getPackageSize(PackageManagerService pm, String pkg) { + PackageInfo info = pm.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + long size = 0; + if (info != null && info.applicationInfo != null) { + File path = Paths.get(info.applicationInfo.sourceDir).toFile(); + if (path.isFile()) { + path = path.getParentFile(); + } + size += getDirectorySize(path); + if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { + for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { + path = Paths.get(splitSourceDir).toFile(); + if (path.isFile()) { + path = path.getParentFile(); + } + size += getDirectorySize(path); + } + } + return size; + } + return 0; + } + private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs, long lowStorageThreshold, boolean is_for_primary_dex, ArraySet<String> failedPackageNames) { @@ -315,8 +364,10 @@ public class BackgroundDexOptService extends JobService { int reason; boolean downgrade; + long package_size_before = 0; //used when the app is downgraded // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { + package_size_before = getPackageSize(pm, pkg); // This applies for system apps or if packages location is not a directory, i.e. // monolithic install. if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) { @@ -366,6 +417,10 @@ public class BackgroundDexOptService extends JobService { synchronized (failedPackageNames) { failedPackageNames.remove(pkg); } + if (downgrade) { + StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, + getPackageSize(pm, pkg), /*aggressive=*/ false); + } } } notifyPinService(updatedPackages); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6baf12ce3b44..330090003e12 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1342,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; @@ -1458,8 +1460,9 @@ public class PackageManagerService extends IPackageManager.Stub Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod, args.traceCookie); } - } else { - Slog.e(TAG, "Bogus post-install token " + msg.arg1); + } else if (DEBUG_INSTALL) { + // No post-install when we run restore from installExistingPackageForUser + Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1); } Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1); @@ -2865,6 +2868,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. @@ -3747,7 +3753,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> @@ -3755,6 +3761,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) { @@ -3772,6 +3779,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; } @@ -5487,13 +5499,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) { @@ -5512,7 +5524,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) { @@ -5567,11 +5579,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; @@ -5687,10 +5699,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; @@ -5725,8 +5737,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; @@ -5753,8 +5766,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; @@ -5802,8 +5815,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; @@ -5824,8 +5838,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; @@ -5845,10 +5860,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(); @@ -12723,6 +12738,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int installExistingPackageAsUser(String packageName, int userId, int installFlags, int installReason) { + if (DEBUG_INSTALL) { + Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId + + " installFlags=" + installFlags + " installReason=" + installReason); + } + final int callingUid = Binder.getCallingUid(); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED @@ -12793,6 +12813,11 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mPackages) { updateSequenceNumberLP(pkgSetting, new int[]{ userId }); } + // start async restore with no post-install since we finish install here + PackageInstalledInfo res = + createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED); + res.pkg = pkgSetting.pkg; + restoreAndPostInstall(userId, res, null); } } finally { Binder.restoreCallingIdentity(callingId); @@ -13732,8 +13757,8 @@ public class PackageManagerService extends IPackageManager.Stub } } for (InstallRequest request : installRequests) { - resolvePackageInstalledInfo(request.args, - request.installResult); + restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, + new PostInstallData(request.args, request.installResult)); } }); } @@ -13748,7 +13773,14 @@ public class PackageManagerService extends IPackageManager.Stub return res; } - private void resolvePackageInstalledInfo(InstallArgs args, PackageInstalledInfo res) { + /** @param data Post-install is performed only if this is non-null. */ + private void restoreAndPostInstall( + int userId, PackageInstalledInfo res, @Nullable PostInstallData data) { + if (DEBUG_INSTALL) { + Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + + res.pkg.packageName); + } + // A restore should be performed at this point if (a) the install // succeeded, (b) the operation is not an update, and (c) the new // package has not opted out of backup participation. @@ -13764,9 +13796,12 @@ public class PackageManagerService extends IPackageManager.Stub int token; if (mNextInstallToken < 0) mNextInstallToken = 1; token = mNextInstallToken++; + if (data != null) { + mRunningInstalls.put(token, data); + } else if (DEBUG_INSTALL) { + Log.v(TAG, "No post-install required for " + token); + } - PostInstallData data = new PostInstallData(args, res); - mRunningInstalls.put(token, data); if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { @@ -13777,7 +13812,11 @@ public class PackageManagerService extends IPackageManager.Stub IBackupManager bm = IBackupManager.Stub.asInterface( 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); } @@ -18944,7 +18983,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; @@ -19135,7 +19175,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) { @@ -19808,6 +19848,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)); + } } } @@ -19960,6 +20008,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) { @@ -23122,6 +23184,8 @@ public class PackageManagerService extends IPackageManager.Stub return mWellbeingPackage; case PackageManagerInternal.PACKAGE_DOCUMENTER: return mDocumenterPackage; + case PackageManagerInternal.PACKAGE_CONFIGURATOR: + return mConfiguratorPackage; } return null; } 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/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 83f0fde066ec..563fd7f90c4b 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -20,8 +20,10 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.content.res.Resources; import android.os.PersistableBundle; import android.text.format.Formatter; @@ -640,6 +642,55 @@ class ShortcutPackage extends ShortcutPackageItem { } /** + * Returns a list of ShortcutInfos that match the given intent filter and the category of + * available ShareTarget definitions in this package. + */ + public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets( + @NonNull IntentFilter filter) { + final List<ShareTargetInfo> matchedTargets = new ArrayList<>(); + for (int i = 0; i < mShareTargets.size(); i++) { + final ShareTargetInfo target = mShareTargets.get(i); + for (ShareTargetInfo.TargetData data : target.mTargetData) { + if (filter.hasDataType(data.mMimeType)) { + // Matched at least with one data type + matchedTargets.add(target); + break; + } + } + } + + if (matchedTargets.isEmpty()) { + return new ArrayList<>(); + } + + // Get the list of all dynamic shortcuts in this package + final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + findAll(shortcuts, ShortcutInfo::isDynamicVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + + final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>(); + for (int i = 0; i < shortcuts.size(); i++) { + final ShortcutInfo si = shortcuts.get(i); + for (int j = 0; j < matchedTargets.size(); j++) { + // Shortcut must have all of share target categories + boolean hasAllCategories = true; + final ShareTargetInfo target = matchedTargets.get(j); + for (int q = 0; q < target.mCategories.length; q++) { + if (!si.getCategories().contains(target.mCategories[q])) { + hasAllCategories = false; + break; + } + } + if (hasAllCategories) { + result.add(new ShortcutManager.ShareShortcutInfo(si, new ComponentName( + getPackageName(), target.mTargetClass))); + break; + } + } + } + return result; + } + + /** * Return the filenames (excluding path names) of icon bitmap files from this package. */ public ArraySet<String> getUsedBitmapFiles() { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 2b773f47e3e9..fdbaba24966b 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.content.pm.ShortcutServiceInternal; import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; import android.content.res.Resources; @@ -2149,6 +2150,23 @@ public class ShortcutService extends IShortcutService.Stub { } } + @Override + public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName, + IntentFilter filter, @UserIdInt int userId) { + verifyCaller(packageName, userId); + + synchronized (mLock) { + throwIfUserLockedL(userId); + + final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>(); + + final ShortcutUser user = getUserShortcutsLocked(userId); + user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter))); + + return new ParceledListSlice<>(shortcutInfoList); + } + } + @GuardedBy("mLock") private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName, @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index bd14223c962c..7bab0bbc7964 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -19,6 +19,7 @@ package com.android.server.pm; import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.ApexInfoList; +import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; @@ -138,7 +139,7 @@ public class StagingManager { return success; } - void preRebootVerification(@NonNull PackageInstallerSession session) { + private void preRebootVerification(@NonNull PackageInstallerSession session) { boolean success = true; if ((session.params.installFlags & PackageManager.INSTALL_APEX) != 0) { @@ -170,6 +171,30 @@ public class StagingManager { } } + private void resumeSession(@NonNull PackageInstallerSession session) { + // Check with apexservice whether the apex + // packages have been activated. + final IApexService apex = IApexService.Stub.asInterface( + ServiceManager.getService("apexservice")); + ApexSessionInfo apexSessionInfo; + try { + apexSessionInfo = apex.getStagedSessionInfo(session.sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + // TODO should we retry here? Mark the session as failed? + return; + } + if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { + session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED); + } + if (apexSessionInfo.isActivated) { + session.setStagedSessionApplied(); + // TODO(b/118865310) if multi-package proceed with the installation of APKs. + } + // TODO(b/118865310) if (apexSessionInfo.isVerified) { /* mark this as staged in apexd */ } + // In every other case apexd will retry to apply the session at next boot. + } + void commitSession(@NonNull PackageInstallerSession session) { updateStoredSession(session); mBgHandler.post(() -> preRebootVerification(session)); @@ -190,8 +215,19 @@ public class StagingManager { void restoreSession(@NonNull PackageInstallerSession session) { updateStoredSession(session); - // TODO(b/118865310): This method is called when PackageInstaller is re-instantiated, e.g. - // at reboot. Staging manager should at this point recover state from apexd and decide what - // to do with the session. + // Check the state of the session and decide what to do next. + if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) { + // Final states, nothing to do. + return; + } + if (!session.isStagedSessionReady()) { + // The framework got restarted before the pre-reboot verification could complete, + // restart the verification. + mBgHandler.post(() -> preRebootVerification(session)); + } else { + // Session had already being marked ready. Start the checks to verify if there is any + // follow-up work. + mBgHandler.post(() -> resumeSession(session)); + } } } 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/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 ff93345a6e8d..41cab2d7ebd3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -480,6 +480,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mShortPressOnSleepBehavior; int mShortPressOnWindowBehavior; boolean mHasSoftInput = false; + boolean mHapticTextHandleEnabled; boolean mUseTvRouting; int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; @@ -1836,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( @@ -2291,9 +2295,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } final LayoutParams attrs = win.getAttrs(); - final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw() && - ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0 - || !canBeHiddenByKeyguardLw(imeTarget)); + final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw() + && (imeTarget.canShowWhenLocked() || !canBeHiddenByKeyguardLw(imeTarget)); // Show IME over the keyguard if the target allows it boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this) @@ -2301,7 +2304,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isKeyguardLocked() && isKeyguardOccluded()) { // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded. - allowWhenLocked |= (attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 + allowWhenLocked |= win.canShowWhenLocked() // Show error dialogs over apps that are shown on lockscreen || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0; } @@ -5201,8 +5204,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: @@ -5367,11 +5373,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 c37254b22ea5..e1a911e8ada5 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -468,6 +468,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** @return true if this window desires key events. */ boolean canReceiveKeys(); + /** @return true if the window can show over keyguard. */ + boolean canShowWhenLocked(); + /** * Writes {@link com.android.server.wm.IdentifierProto} to stream. */ 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/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java index f7cc4432f9bc..2700f9ddc203 100644 --- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java @@ -24,7 +24,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.TrafficStats; import android.os.Binder; import android.os.Environment; import android.os.FileObserver; @@ -42,13 +41,13 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.DataUnit; import android.util.Slog; +import android.util.StatsLog; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; -import com.android.server.IoThread; import com.android.server.SystemService; import com.android.server.pm.InstructionSets; import com.android.server.pm.PackageManagerService; @@ -499,9 +498,15 @@ public class DeviceStorageMonitorService extends SystemService { notification.flags |= Notification.FLAG_NO_CLEAR; mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE, notification, UserHandle.ALL); + StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED, + Objects.toString(vol.getDescription()), + StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON); } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) { mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE, UserHandle.ALL); + StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED, + Objects.toString(vol.getDescription()), + StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF); } } diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java index 7236d79c55ab..d4aa59d3ce85 100644 --- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java +++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java @@ -16,6 +16,8 @@ package com.android.server.textservices; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -513,8 +515,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // TODO: Save SpellCheckerService by supported languages. Currently only one spell // checker is saved. @Override - public SpellCheckerInfo getCurrentSpellChecker(String locale) { - int userId = UserHandle.getCallingUserId(); + public SpellCheckerInfo getCurrentSpellChecker(@UserIdInt int userId, String locale) { + verifyUser(userId); synchronized (mLock) { final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; @@ -527,11 +529,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale". @Override public SpellCheckerSubtype getCurrentSpellCheckerSubtype( - boolean allowImplicitlySelectedSubtype) { + @UserIdInt int userId, boolean allowImplicitlySelectedSubtype) { + verifyUser(userId); + final int subtypeHashCode; final SpellCheckerInfo sci; final Locale systemLocale; - final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); @@ -591,17 +594,17 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } @Override - public void getSpellCheckerService(String sciId, String locale, + public void getSpellCheckerService(@UserIdInt int userId, String sciId, String locale, ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, Bundle bundle) { + verifyUser(userId); if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) { Slog.e(TAG, "getSpellCheckerService: Invalid input."); return; } - int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { - final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return; HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap; @@ -634,8 +637,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } @Override - public boolean isSpellCheckerEnabled() { - int userId = UserHandle.getCallingUserId(); + public boolean isSpellCheckerEnabled(@UserIdInt int userId) { + verifyUser(userId); synchronized (mLock) { final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); @@ -671,11 +674,11 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } @Override - public SpellCheckerInfo[] getEnabledSpellCheckers() { - int callingUserId = UserHandle.getCallingUserId(); + public SpellCheckerInfo[] getEnabledSpellCheckers(@UserIdInt int userId) { + verifyUser(userId); synchronized (mLock) { - final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList; @@ -691,11 +694,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } @Override - public void finishSpellCheckerService(ISpellCheckerSessionListener listener) { + public void finishSpellCheckerService(@UserIdInt int userId, + ISpellCheckerSessionListener listener) { if (DBG) { Slog.d(TAG, "FinishSpellCheckerService"); } - int userId = UserHandle.getCallingUserId(); + verifyUser(userId); synchronized (mLock) { final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); @@ -716,6 +720,15 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } + private void verifyUser(@UserIdInt int userId) { + final int callingUserId = UserHandle.getCallingUserId(); + if (userId != callingUserId) { + mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, + "Cross-user interaction requires INTERACT_ACROSS_USERS_FULL. userId=" + userId + + " callingUserId=" + callingUserId); + } + } + private void setCurrentSpellCheckerLocked(@Nullable SpellCheckerInfo sci, TextServicesData tsd) { final String sciId = (sci != null) ? sci.getId() : ""; if (DBG) { diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index c01a42b05366..e817dd47e756 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -629,6 +629,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) { @@ -639,9 +643,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) { @@ -649,15 +657,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) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index da78469b9d45..6213fa02cb9f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2562,6 +2562,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/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 24d3d17a54e8..a83ef34f1cac 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2638,6 +2638,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/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 089640b32dd3..5f393ef59057 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -65,6 +65,8 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation; import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation; +import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE; +import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -73,8 +75,6 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; -import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE; -import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION; import android.annotation.DrawableRes; import android.annotation.NonNull; @@ -84,6 +84,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.ResourceId; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -547,7 +548,7 @@ public class AppTransition implements Dump { } Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { - int resId = ResourceId.ID_NULL; + int resId = Resources.ID_NULL; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations(lp); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index b735115be61e..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; } /** @@ -1336,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 @@ -1368,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, @@ -1538,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); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 7aabc15d9860..bc165dceb544 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -676,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/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 28931d6deb10..c4a853dc3483 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(); diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index 905787051de8..2e5df45f9080 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -31,6 +31,10 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.server.wm.WindowManagerService.H; +/** + * 1. Adjust the top most focus display if touch down on some display. + * 2. Adjust the pointer icon when cursor moves to the task bounds. + */ public class TaskTapPointerEventListener implements PointerEventListener { private final Region mTouchExcludeRegion = new Region(); @@ -80,8 +84,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { if (motionEvent.getDisplayId() != getDisplayId()) { return; } - final int action = motionEvent.getAction(); - switch (action & MotionEvent.ACTION_MASK) { + switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: { final int x = (int) motionEvent.getX(); final int y = (int) motionEvent.getY(); @@ -97,7 +100,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { } } break; - + case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: { final int x = (int) motionEvent.getX(); final int y = (int) motionEvent.getY(); @@ -125,6 +128,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { mPointerIconType = iconType; if (mPointerIconType == TYPE_NOT_SPECIFIED) { // Find the underlying window and ask it restore the pointer icon. + mService.mH.removeMessages(H.RESTORE_POINTER_ICON); mService.mH.obtainMessage(H.RESTORE_POINTER_ICON, x, y, mDisplayContent).sendToTarget(); } else { @@ -133,6 +137,18 @@ public class TaskTapPointerEventListener implements PointerEventListener { } } break; + case MotionEvent.ACTION_HOVER_EXIT: { + final int x = (int) motionEvent.getX(); + final int y = (int) motionEvent.getY(); + if (mPointerIconType != TYPE_NOT_SPECIFIED) { + mPointerIconType = TYPE_NOT_SPECIFIED; + // Find the underlying window and ask it to restore the pointer icon. + mService.mH.removeMessages(H.RESTORE_POINTER_ICON); + mService.mH.obtainMessage(H.RESTORE_POINTER_ICON, + x, y, mDisplayContent).sendToTarget(); + } + } + break; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0c4f4961d215..fda7a85c1270 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5943,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="); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index cd29b3c29248..8f86c003e4b0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2405,6 +2405,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && !cantReceiveTouchInput(); } + @Override + public boolean canShowWhenLocked() { + final boolean showBecauseOfActivity = + mAppToken != null && mAppToken.mActivityRecord.canShowWhenLocked(); + final boolean showBecauseOfWindow = (getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0; + return showBecauseOfActivity || showBecauseOfWindow; + } + /** @return false if this window desires touch events. */ boolean cantReceiveTouchInput() { return mAppToken != null && mAppToken.getTask() != null diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 641200769cf0..90c9cc2b0a35 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -111,6 +111,7 @@ static struct { jmethodID getKeyboardLayoutOverlay; jmethodID getDeviceAlias; jmethodID getTouchCalibrationForInputDevice; + jmethodID getContextForDisplay; } gServiceClassInfo; static struct { @@ -260,17 +261,16 @@ public: /* --- PointerControllerPolicyInterface implementation --- */ - virtual void loadPointerIcon(SpriteIcon* icon); - virtual void loadPointerResources(PointerResources* outResources); + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId); + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId); virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, - std::map<int32_t, PointerAnimation>* outAnimationResources); + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId); virtual int32_t getDefaultPointerIconId(); virtual int32_t getCustomPointerIconId(); private: sp<InputManager> mInputManager; - jobject mContextObj; jobject mServiceObj; sp<Looper> mLooper; @@ -329,7 +329,6 @@ NativeInputManager::NativeInputManager(jobject contextObj, mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); - mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { @@ -351,7 +350,6 @@ NativeInputManager::NativeInputManager(jobject contextObj, NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); - env->DeleteGlobalRef(mContextObj); env->DeleteGlobalRef(mServiceObj); } @@ -1202,19 +1200,22 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant( return result; } -void NativeInputManager::loadPointerIcon(SpriteIcon* icon) { +void NativeInputManager::loadPointerIcon(SpriteIcon* icon, int32_t displayId) { ATRACE_CALL(); JNIEnv* env = jniEnv(); ScopedLocalRef<jobject> pointerIconObj(env, env->CallObjectMethod( - mServiceObj, gServiceClassInfo.getPointerIcon)); + mServiceObj, gServiceClassInfo.getPointerIcon, displayId)); if (checkAndClearExceptionFromCallback(env, "getPointerIcon")) { return; } + ScopedLocalRef<jobject> displayContext(env, env->CallObjectMethod( + mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); + PointerIcon pointerIcon; status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(), - mContextObj, &pointerIcon); + displayContext.get(), &pointerIcon); if (!status && !pointerIcon.isNullIcon()) { *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY); } else { @@ -1222,28 +1223,34 @@ void NativeInputManager::loadPointerIcon(SpriteIcon* icon) { } } -void NativeInputManager::loadPointerResources(PointerResources* outResources) { +void NativeInputManager::loadPointerResources(PointerResources* outResources, int32_t displayId) { ATRACE_CALL(); JNIEnv* env = jniEnv(); - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER, + ScopedLocalRef<jobject> displayContext(env, env->CallObjectMethod( + mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); + + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_HOVER, &outResources->spotHover); - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH, + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_TOUCH, &outResources->spotTouch); - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR, + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_ANCHOR, &outResources->spotAnchor); } void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, - std::map<int32_t, PointerAnimation>* outAnimationResources) { + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) { ATRACE_CALL(); JNIEnv* env = jniEnv(); + ScopedLocalRef<jobject> displayContext(env, env->CallObjectMethod( + mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); + for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING; ++iconId) { PointerIcon pointerIcon; loadSystemIconAsSpriteWithPointerIcon( - env, mContextObj, iconId, &pointerIcon, &((*outResources)[iconId])); + env, displayContext.get(), iconId, &pointerIcon, &((*outResources)[iconId])); if (!pointerIcon.bitmapFrames.empty()) { PointerAnimation& animationData = (*outAnimationResources)[iconId]; size_t numFrames = pointerIcon.bitmapFrames.size() + 1; @@ -1258,7 +1265,7 @@ void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIc } } } - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL, + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_NULL, &((*outResources)[POINTER_ICON_STYLE_NULL])); } @@ -1819,7 +1826,7 @@ int register_android_server_InputManager(JNIEnv* env) { "getPointerLayer", "()I"); GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz, - "getPointerIcon", "()Landroid/view/PointerIcon;"); + "getPointerIcon", "(I)Landroid/view/PointerIcon;"); GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz, "getPointerDisplayId", "()I"); @@ -1835,6 +1842,10 @@ int register_android_server_InputManager(JNIEnv* env) { "getTouchCalibrationForInputDevice", "(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;"); + GET_METHOD_ID(gServiceClassInfo.getContextForDisplay, clazz, + "getContextForDisplay", + "(I)Landroid/content/Context;") + // InputDevice FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index dcb2ff5f9df5..f3c19d099851 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -81,7 +81,7 @@ static jmethodID method_correctionSatFlags; static jmethodID method_correctionSatConstType; static jmethodID method_correctionSatId; static jmethodID method_correctionSatCarrierFreq; -static jmethodID method_correctionSatIsLos; +static jmethodID method_correctionSatIsLosProb; static jmethodID method_correctionSatEpl; static jmethodID method_correctionSatEplUnc; static jmethodID method_correctionSatRefPlane; @@ -2277,8 +2277,8 @@ static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measuremen singleSatCorrClass, "getSatId", "()I"); method_correctionSatCarrierFreq = env->GetMethodID( singleSatCorrClass, "getCarrierFrequencyHz", "()F"); - method_correctionSatIsLos = env->GetMethodID( - singleSatCorrClass,"getSatIsLos", "()Z"); + method_correctionSatIsLosProb = env->GetMethodID( + singleSatCorrClass,"getProbSatIsLos", "()F"); method_correctionSatEpl = env->GetMethodID( singleSatCorrClass, "getExcessPathLengthMeters", "()F"); method_correctionSatEplUnc = env->GetMethodID( @@ -2296,8 +2296,8 @@ static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measuremen env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId); jfloat carrierFreqHz = env->CallFloatMethod( singleSatCorrectionObj, method_correctionSatCarrierFreq); - jboolean satIsLos = env->CallBooleanMethod(singleSatCorrectionObj, - method_correctionSatIsLos); + jfloat probSatIsLos = env->CallFloatMethod(singleSatCorrectionObj, + method_correctionSatIsLosProb); jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, @@ -2337,7 +2337,7 @@ static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measuremen .constellation = static_cast<GnssConstellationType>(constType), .svid = static_cast<uint16_t>(satId), .carrierFrequencyHz = carrierFreqHz, - .satIsLos = static_cast<bool>(satIsLos), + .probSatIsLos = probSatIsLos, .excessPathLengthMeters = eplMeters, .excessPathLengthUncertaintyMeters = eplUncMeters, .reflectingPlane = reflectingPlane, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index fef2db921dcf..cef47caff740 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -74,6 +74,7 @@ import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.ILockSettings; import com.android.server.am.ActivityManagerService; import com.android.server.appbinding.AppBindingService; +import com.android.server.attention.AttentionManagerService; import com.android.server.audio.AudioService; import com.android.server.biometrics.BiometricService; import com.android.server.biometrics.face.FaceService; @@ -148,10 +149,11 @@ import com.android.server.webkit.WebViewUpdateService; import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowManagerGlobalLock; import com.android.server.wm.WindowManagerService; -import com.google.android.startop.iorap.IorapForwardingService; import dalvik.system.VMRuntime; +import com.google.android.startop.iorap.IorapForwardingService; + import java.io.File; import java.io.IOException; import java.util.Locale; @@ -1230,6 +1232,10 @@ public final class SystemServer { traceEnd(); } + traceBeginAndSlog("StartAttentionManagerService"); + mSystemServiceManager.startService(AttentionManagerService.class); + traceEnd(); + traceBeginAndSlog("StartNetworkScoreService"); mSystemServiceManager.startService(NetworkScoreService.Lifecycle.class); traceEnd(); 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/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index 4a48468867df..c8e67820ce46 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -85,7 +85,6 @@ 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; @@ -794,7 +793,6 @@ public class AppStateTrackerTest { } @Test - @FlakyTest(bugId = 114098433) public void testAllListeners() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); diff --git a/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java new file mode 100644 index 000000000000..e51884429718 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.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.server.display; + +import static com.android.server.display.AppSaturationController.TRANSLATION_VECTOR; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.os.UserHandle; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.display.ColorDisplayService.ColorTransformController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.lang.ref.WeakReference; + +@RunWith(AndroidJUnit4.class) +public class AppSaturationControllerTest { + + private static final String TEST_PACKAGE_NAME = "com.android.test"; + + private int mUserId; + private AppSaturationController mAppSaturationController; + private float[] mMatrix; + + @Mock + private ColorTransformController mColorTransformController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mUserId = ActivityManager.getCurrentUser(); + mAppSaturationController = new AppSaturationController(); + mMatrix = new float[9]; + } + + @After + public void tearDown() { + mAppSaturationController = null; + mUserId = UserHandle.USER_NULL; + } + + @Test + public void addColorTransformController_appliesExistingSaturation() { + final WeakReference<ColorTransformController> ref = new WeakReference<>( + mColorTransformController); + mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30); + mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref); + AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix); + verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR)); + } + + @Test + public void setSaturationLevel_resetToDefault() { + final WeakReference<ColorTransformController> ref = new WeakReference<>( + mColorTransformController); + mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref); + verify(mColorTransformController, never()) + .applyAppSaturation(any(), eq(TRANSLATION_VECTOR)); + mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30); + AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix); + verify(mColorTransformController, times(1)) + .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR)); + mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100); + AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix); + verify(mColorTransformController, times(2)) + .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR)); + } + + @Test + public void setSaturationLevel_updateLevel() { + final WeakReference<ColorTransformController> ref = new WeakReference<>( + mColorTransformController); + mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref); + verify(mColorTransformController, never()) + .applyAppSaturation(any(), eq(TRANSLATION_VECTOR)); + mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30); + AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix); + verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR)); + mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 70); + AppSaturationController.computeGrayscaleTransformMatrix(.7f, mMatrix); + verify(mColorTransformController, times(2)) + .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR)); + mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100); + AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix); + verify(mColorTransformController, times(3)) + .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java new file mode 100644 index 000000000000..e5529cb899cc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.job; + +import static com.google.common.truth.Truth.assertThat; + +import android.util.Log; + +import com.android.server.job.JobConcurrencyManager.JobCountTracker; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Random; + +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +/** + * Test for {@link com.android.server.job.JobConcurrencyManager.JobCountTracker}. + */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class JobCountTrackerTest { + private static final String TAG = "JobCountTrackerTest"; + + private Random mRandom; + private JobCountTracker mJobCountTracker; + + @Before + public void setUp() { + mRandom = new Random(1); // Always use the same series of pseudo random values. + mJobCountTracker = new JobCountTracker(); + } + + /** + * Represents running and pending jobs. + */ + class Jobs { + public int runningFg; + public int runningBg; + public int pendingFg; + public int pendingBg; + + public void maybeEnqueueJobs(double startRatio, double fgJobRatio) { + while (mRandom.nextDouble() < startRatio) { + if (mRandom.nextDouble() < fgJobRatio) { + pendingFg++; + } else { + pendingBg++; + } + } + } + + public void maybeFinishJobs(double stopRatio) { + for (int i = runningBg; i > 0; i--) { + if (mRandom.nextDouble() < stopRatio) { + runningBg--; + } + } + for (int i = runningFg; i > 0; i--) { + if (mRandom.nextDouble() < stopRatio) { + runningFg--; + } + } + } + } + + + private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) { + mJobCountTracker.reset(totalMax, maxBg, minBg); + + for (int i = 0; i < jobs.runningFg; i++) { + mJobCountTracker.incrementRunningJobCount(true); + } + for (int i = 0; i < jobs.runningBg; i++) { + mJobCountTracker.incrementRunningJobCount(false); + } + + for (int i = 0; i < jobs.pendingFg; i++) { + mJobCountTracker.incrementPendingJobCount(true); + } + for (int i = 0; i < jobs.pendingBg; i++) { + mJobCountTracker.incrementPendingJobCount(false); + } + + mJobCountTracker.onCountDone(); + + while ((jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) + || (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false))) { + final boolean isStartingFg = mRandom.nextBoolean(); + + if (isStartingFg) { + if (jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) { + jobs.pendingFg--; + jobs.runningFg++; + mJobCountTracker.onStartingNewJob(true); + } + } else { + if (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false)) { + jobs.pendingBg--; + jobs.runningBg++; + mJobCountTracker.onStartingNewJob(false); + } + } + } + + Log.i(TAG, "" + mJobCountTracker); + } + + /** + * Used by the following testRandom* tests. + */ + private void checkRandom(Jobs jobs, int numTests, int totalMax, int maxBg, int minBg, + double startRatio, double fgJobRatio, double stopRatio) { + for (int i = 0; i < numTests; i++) { + + jobs.maybeFinishJobs(stopRatio); + jobs.maybeEnqueueJobs(startRatio, fgJobRatio); + + startPendingJobs(jobs, totalMax, maxBg, minBg); + + assertThat(jobs.runningFg).isAtMost(totalMax); + assertThat(jobs.runningBg).isAtMost(totalMax); + assertThat(jobs.runningFg + jobs.runningBg).isAtMost(totalMax); + assertThat(jobs.runningBg).isAtMost(maxBg); + } + } + + /** + * Randomly enqueue / stop jobs and make sure we won't run more jobs than we should. + */ + @Test + public void testRandom1() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final int maxBg = 4; + final int minBg = 2; + final double stopRatio = 0.1; + final double fgJobRatio = 0.5; + final double startRatio = 0.1; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio , stopRatio); + } + + @Test + public void testRandom2() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 2; + final int maxBg = 2; + final int minBg = 0; + final double stopRatio = 0.5; + final double fgJobRatio = 0.5; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + @Test + public void testRandom3() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 2; + final int maxBg = 2; + final int minBg = 2; + final double stopRatio = 0.5; + final double fgJobRatio = 0.5; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + @Test + public void testRandom4() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 10; + final int maxBg = 2; + final int minBg = 0; + final double stopRatio = 0.5; + final double fgJobRatio = 0.5; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + @Test + public void testRandom5() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final int maxBg = 4; + final int minBg = 2; + final double stopRatio = 0.5; + final double fgJobRatio = 0.1; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + @Test + public void testRandom6() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final int maxBg = 4; + final int minBg = 2; + final double stopRatio = 0.5; + final double fgJobRatio = 0.9; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + @Test + public void testRandom7() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final int maxBg = 4; + final int minBg = 2; + final double stopRatio = 0.4; + final double fgJobRatio = 0.1; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + @Test + public void testRandom8() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final int maxBg = 4; + final int minBg = 2; + final double stopRatio = 0.4; + final double fgJobRatio = 0.9; + final double startRatio = 0.5; + + checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio); + } + + /** Used by the following tests */ + private void checkSimple(int totalMax, int maxBg, int minBg, + int runningFg, int runningBg, int pendingFg, int pendingBg, + int resultRunningFg, int resultRunningBg, int resultPendingFg, int resultPendingBg) { + final Jobs jobs = new Jobs(); + jobs.runningFg = runningFg; + jobs.runningBg = runningBg; + jobs.pendingFg = pendingFg; + jobs.pendingBg = pendingBg; + + startPendingJobs(jobs, totalMax, maxBg, minBg); + + assertThat(jobs.runningFg).isEqualTo(resultRunningFg); + assertThat(jobs.runningBg).isEqualTo(resultRunningBg); + + assertThat(jobs.pendingFg).isEqualTo(resultPendingFg); + assertThat(jobs.pendingBg).isEqualTo(resultPendingBg); + } + + + @Test + public void testBasic() { + // Args are: + // First 3: Total-max, bg-max, bg-min. + // Next 2: Running FG / BG + // Next 2: Pending FG / BG + // Next 4: Result running FG / BG, pending FG/BG. + checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 1, 0, /*res run/pen=*/ 1, 0, 0, 0); + + checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 0, /*res run/pen=*/ 6, 0, 4, 0); + + // When there are BG jobs pending, 2 (min-BG) jobs should run. + checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0); + checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1); + + checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java new file mode 100644 index 000000000000..01199a36ff5b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.job; + +import android.util.KeyValueListParser; + +import com.android.server.job.JobSchedulerService.MaxJobCounts; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MaxJobCountsTest { + + private void check(String config, + int defaultTotal, int defaultMaxBg, int defaultMinBg, + int expectedTotal, int expectedMaxBg, int expectedMinBg) { + final KeyValueListParser parser = new KeyValueListParser(','); + parser.setString(config); + + final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts( + defaultTotal, "total", + defaultMaxBg, "maxbg", + defaultMinBg, "minbg"); + + counts.parse(parser); + + Assert.assertEquals(expectedTotal, counts.getTotalMax()); + Assert.assertEquals(expectedMaxBg, counts.getMaxBg()); + Assert.assertEquals(expectedMinBg, counts.getMinBg()); + } + + @Test + public void test() { + check("", /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0); + check("", /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0); + check("", /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0); + check("", /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0); + check("", /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4); + check("", /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5); + check("", /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3); + check("", /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1); + + check("total=5,maxbg=4,minbg=3", /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3); + check("total=5", /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4); + check("maxbg=4", /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4); + check("minbg=3", /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index 8a9e5d181777..c2d4846b14c0 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -23,6 +23,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FOR import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; @@ -91,6 +92,9 @@ public class KeySyncTaskTest { private static final byte[] TEST_VAULT_HANDLE = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; private static final String TEST_APP_KEY_ALIAS = "rcleaver"; + private static final byte[] TEST_APP_KEY_METADATA_NULL = null; + private static final byte[] TEST_APP_KEY_METADATA_NON_NULL = + "mdata".getBytes(StandardCharsets.UTF_8); private static final int TEST_GENERATION_ID = 2; private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PATTERN; private static final String TEST_CREDENTIAL = "pas123"; @@ -251,7 +255,7 @@ public class KeySyncTaskTest { TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS, - WrappedKey.fromSecretKey(mEncryptKey, applicationKey)); + WrappedKey.fromSecretKey(mEncryptKey, applicationKey, TEST_APP_KEY_METADATA_NULL)); when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); mKeySyncTask.run(); @@ -267,7 +271,7 @@ public class KeySyncTaskTest { TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS, - WrappedKey.fromSecretKey(mEncryptKey, applicationKey)); + WrappedKey.fromSecretKey(mEncryptKey, applicationKey, TEST_APP_KEY_METADATA_NULL)); mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); @@ -545,18 +549,20 @@ public class KeySyncTaskTest { assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias()); assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias()); byte[] appKey = KeySyncUtils.decryptApplicationKey( - recoveryKey, keyData.getEncryptedKeyMaterial()); + recoveryKey, keyData.getEncryptedKeyMaterial(), TEST_APP_KEY_METADATA_NULL); assertThat(appKey).isEqualTo(applicationKey.getEncoded()); } @Test - public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception { + public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath_withNullKeyMetadata() + throws Exception { mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); mRecoverableKeyStoreDb.setServerParams( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); - addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); + addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS, + TEST_APP_KEY_METADATA_NULL); mKeySyncTask.run(); @@ -564,6 +570,33 @@ public class KeySyncTaskTest { verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); assertThat(applicationKeys).hasSize(1); + WrappedApplicationKey keyData = applicationKeys.get(0); + assertThat(keyData.getAlias()).isEqualTo(TEST_APP_KEY_ALIAS); + assertThat(keyData.getMetadata()).isEqualTo(TEST_APP_KEY_METADATA_NULL); + assertThat(keyChainSnapshot.getTrustedHardwareCertPath()) + .isEqualTo(TestData.CERT_PATH_1); + } + + @Test + public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath_withNonNullKeyMetadata() + throws Exception { + mRecoverableKeyStoreDb.setRecoveryServiceCertPath( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); + mRecoverableKeyStoreDb.setServerParams( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); + when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); + addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS, + TEST_APP_KEY_METADATA_NON_NULL); + + mKeySyncTask.run(); + + KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); + verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); + List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); + assertThat(applicationKeys).hasSize(1); + WrappedApplicationKey keyData = applicationKeys.get(0); + assertThat(keyData.getAlias()).isEqualTo(TEST_APP_KEY_ALIAS); + assertThat(keyData.getMetadata()).isEqualTo(TEST_APP_KEY_METADATA_NON_NULL); assertThat(keyChainSnapshot.getTrustedHardwareCertPath()) .isEqualTo(TestData.CERT_PATH_1); } @@ -788,6 +821,11 @@ public class KeySyncTaskTest { private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias) throws Exception{ + return addApplicationKey(userId, recoveryAgentUid, alias, TEST_APP_KEY_METADATA_NULL); + } + + private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias, + byte[] metadata) throws Exception { SecretKey applicationKey = generateKey(); mRecoverableKeyStoreDb.setServerParams( userId, recoveryAgentUid, TEST_VAULT_HANDLE); @@ -800,7 +838,7 @@ public class KeySyncTaskTest { userId, recoveryAgentUid, alias, - WrappedKey.fromSecretKey(mEncryptKey, applicationKey)); + WrappedKey.fromSecretKey(mEncryptKey, applicationKey, metadata)); return applicationKey; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java index f832d3ceef8e..178fd104a1ae 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import android.util.Pair; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -65,7 +67,8 @@ public class KeySyncUtilsTest { private static final byte[] RECOVERY_RESPONSE_HEADER = "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8); private static final int PUBLIC_KEY_LENGTH_BYTES = 65; - + private static final byte[] NULL_METADATA = null; + private static final byte[] NON_NULL_METADATA = "somemetadata".getBytes(StandardCharsets.UTF_8); @Test public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception { @@ -125,18 +128,35 @@ public class KeySyncUtilsTest { } @Test - public void decryptApplicationKey_decryptsAnApplicationKeyEncryptedWithSecureBox() - throws Exception { + public void decryptApplicationKey_decryptsAnApplicationKey_nullMetadata() throws Exception { String alias = "phoebe"; SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); SecretKey applicationKey = generateApplicationKey(); Map<String, byte[]> encryptedKeys = KeySyncUtils.encryptKeysWithRecoveryKey( - recoveryKey, ImmutableMap.of(alias, applicationKey)); + recoveryKey, + ImmutableMap.of(alias, Pair.create(applicationKey, NULL_METADATA))); byte[] encryptedKey = encryptedKeys.get(alias); - byte[] keyMaterial = - KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), encryptedKey); + byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), + encryptedKey, NULL_METADATA); + + assertArrayEquals(applicationKey.getEncoded(), keyMaterial); + } + + @Test + public void decryptApplicationKey_decryptsAnApplicationKey_nonNullMetadata() throws Exception { + String alias = "phoebe"; + SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); + SecretKey applicationKey = generateApplicationKey(); + Map<String, byte[]> encryptedKeys = + KeySyncUtils.encryptKeysWithRecoveryKey( + recoveryKey, + ImmutableMap.of(alias, Pair.create(applicationKey, NON_NULL_METADATA))); + byte[] encryptedKey = encryptedKeys.get(alias); + + byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), + encryptedKey, NON_NULL_METADATA); assertArrayEquals(applicationKey.getEncoded(), keyMaterial); } @@ -147,12 +167,13 @@ public class KeySyncUtilsTest { Map<String, byte[]> encryptedKeys = KeySyncUtils.encryptKeysWithRecoveryKey( KeySyncUtils.generateRecoveryKey(), - ImmutableMap.of("casper", generateApplicationKey())); + ImmutableMap.of("casper", + Pair.create(generateApplicationKey(), NULL_METADATA))); byte[] encryptedKey = encryptedKeys.get(alias); try { - KeySyncUtils.decryptApplicationKey( - KeySyncUtils.generateRecoveryKey().getEncoded(), encryptedKey); + KeySyncUtils.decryptApplicationKey(KeySyncUtils.generateRecoveryKey().getEncoded(), + encryptedKey, NULL_METADATA); fail("Did not throw decrypting with bad key."); } catch (AEADBadTagException error) { // expected @@ -160,6 +181,47 @@ public class KeySyncUtilsTest { } @Test + public void decryptApplicationKey_throwsIfWrongMetadata() throws Exception { + String alias1 = "casper1"; + String alias2 = "casper2"; + String alias3 = "casper3"; + SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); + + Map<String, byte[]> encryptedKeys = + KeySyncUtils.encryptKeysWithRecoveryKey( + recoveryKey, + ImmutableMap.of( + alias1, + Pair.create(generateApplicationKey(), NULL_METADATA), + alias2, + Pair.create(generateApplicationKey(), NON_NULL_METADATA), + alias3, + Pair.create(generateApplicationKey(), NON_NULL_METADATA))); + + try { + KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), + encryptedKeys.get(alias1), NON_NULL_METADATA); + fail("Did not throw decrypting with wrong metadata."); + } catch (AEADBadTagException error) { + // expected + } + try { + KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), + encryptedKeys.get(alias2), NULL_METADATA); + fail("Did not throw decrypting with wrong metadata."); + } catch (AEADBadTagException error) { + // expected + } + try { + KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), + encryptedKeys.get(alias3), "different".getBytes(StandardCharsets.UTF_8)); + fail("Did not throw decrypting with wrong metadata."); + } catch (AEADBadTagException error) { + // expected + } + } + + @Test public void decryptRecoveryKey_decryptsALocallyEncryptedKey() throws Exception { SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey( diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java index 48afb8b6abcb..c295177814b0 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import android.content.Context; import android.security.keystore.AndroidKeyStoreSecretKey; @@ -60,6 +61,9 @@ public class RecoverableKeyGeneratorTest { private static final int TEST_USER_ID = 1000; private static final int KEYSTORE_UID_SELF = -1; private static final int GCM_TAG_LENGTH_BITS = 128; + private static final byte[] NULL_METADATA = null; + private static final byte[] NON_NULL_METADATA = "test-metadata".getBytes( + StandardCharsets.UTF_8); private PlatformEncryptionKey mPlatformKey; private PlatformDecryptionKey mDecryptKey; @@ -90,18 +94,29 @@ public class RecoverableKeyGeneratorTest { } @Test - public void generateAndStoreKey_storesWrappedKey() throws Exception { + public void generateAndStoreKey_storesWrappedKey_nullMetadata() throws Exception { mRecoverableKeyGenerator.generateAndStoreKey( - mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NULL_METADATA); WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS); assertNotNull(wrappedKey); + assertNull(wrappedKey.getKeyMetadata()); + } + + @Test + public void generateAndStoreKey_storesWrappedKey_nonNullMetadata() throws Exception { + mRecoverableKeyGenerator.generateAndStoreKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NON_NULL_METADATA); + + WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS); + assertNotNull(wrappedKey); + assertArrayEquals(NON_NULL_METADATA, wrappedKey.getKeyMetadata()); } @Test public void generateAndStoreKey_returnsRawMaterialOfCorrectLength() throws Exception { byte[] rawKey = mRecoverableKeyGenerator.generateAndStoreKey( - mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NON_NULL_METADATA); assertEquals(KEY_SIZE_BYTES, rawKey.length); } @@ -109,7 +124,7 @@ public class RecoverableKeyGeneratorTest { @Test public void generateAndStoreKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception { byte[] rawMaterial = mRecoverableKeyGenerator.generateAndStoreKey( - mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NULL_METADATA); WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS); Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM); @@ -120,10 +135,30 @@ public class RecoverableKeyGeneratorTest { } @Test + public void importKey_storesNullMetadata() throws Exception { + mRecoverableKeyGenerator.importKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, + randomBytes(KEY_SIZE_BYTES), + NULL_METADATA); + assertNull(mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS).getKeyMetadata()); + } + + @Test + public void importKey_storesNonNullMetadata() throws Exception { + mRecoverableKeyGenerator.importKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, + randomBytes(KEY_SIZE_BYTES), + NON_NULL_METADATA); + assertArrayEquals(NON_NULL_METADATA, + mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS).getKeyMetadata()); + } + + @Test public void importKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception { byte[] rawMaterial = randomBytes(KEY_SIZE_BYTES); mRecoverableKeyGenerator.importKey( - mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial); + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial, + NULL_METADATA); WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS); Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM); @@ -145,10 +180,6 @@ public class RecoverableKeyGeneratorTest { return (AndroidKeyStoreSecretKey) keyGenerator.generateKey(); } - private static byte[] getUtf8Bytes(String s) { - return s.getBytes(StandardCharsets.UTF_8); - } - private static byte[] randomBytes(int n) { byte[] bytes = new byte[n]; new Random().nextBytes(bytes); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index b15863dd3613..c78b96d2d294 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -52,6 +52,7 @@ import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.RecoveryCertPath; import android.security.keystore.recovery.TrustedRootCertificates; import android.security.keystore.recovery.WrappedApplicationKey; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -134,6 +135,8 @@ public class RecoverableKeyStoreManagerTest { private static final int GENERATION_ID = 1; private static final byte[] NONCE = getUtf8Bytes("nonce"); private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial"); + private static final byte[] KEY_METADATA_NULL = null; + private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata"); private static final String KEY_ALGORITHM = "AES"; private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey"; @@ -231,6 +234,77 @@ public class RecoverableKeyStoreManagerTest { } @Test + public void importKeyWithMetadata_nullMetadata_storesTheKey() throws Exception { + int uid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); + byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES); + + mRecoverableKeyStoreManager.importKeyWithMetadata( + TEST_ALIAS, keyMaterial, KEY_METADATA_NULL); + + assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); + assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); + } + + @Test + public void importKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception { + int uid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); + byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES); + + mRecoverableKeyStoreManager.importKeyWithMetadata( + TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL); + + assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); + assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); + } + + @Test + public void importKeyWithMetadata_throwsIfInvalidLength() throws Exception { + byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1); + try { + mRecoverableKeyStoreManager.importKeyWithMetadata( + TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL); + fail("should have thrown"); + } catch (ServiceSpecificException e) { + assertThat(e.getMessage()).contains("not contain 256 bits"); + } + } + + @Test + public void importKeyWithMetadata_throwsIfNullKey() throws Exception { + try { + mRecoverableKeyStoreManager.importKeyWithMetadata( + TEST_ALIAS, /*keyBytes=*/ null, KEY_METADATA_NON_NULL); + fail("should have thrown"); + } catch (NullPointerException e) { + assertThat(e.getMessage()).contains("is null"); + } + } + + @Test + public void generateKeyWithMetadata_nullMetadata_storesTheKey() throws Exception { + int uid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); + + mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL); + + assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); + assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); + } + + @Test + public void generateKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception { + int uid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); + + mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NON_NULL); + + assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); + assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); + } + + @Test public void removeKey_removesAKey() throws Exception { int uid = Binder.getCallingUid(); mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); @@ -1143,7 +1217,10 @@ public class RecoverableKeyStoreManagerTest { int status = 100; int status2 = 200; String alias = "key1"; - WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status); + byte[] keyMetadata = null; + + WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID, + status); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); Map<String, Integer> statuses = mRecoverableKeyStoreManager.getRecoveryStatus(); @@ -1169,7 +1246,8 @@ public class RecoverableKeyStoreManagerTest { private static byte[] encryptedApplicationKey( SecretKey recoveryKey, byte[] applicationKey) throws Exception { return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of( - TEST_ALIAS, new SecretKeySpec(applicationKey, "AES") + TEST_ALIAS, + Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null) )).get(TEST_ALIAS); } @@ -1203,7 +1281,7 @@ public class RecoverableKeyStoreManagerTest { private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion) throws Exception{ - mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL); // Simulate key sync. mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion); mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java index 944d6e0512eb..9b4c3beac582 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java @@ -3,6 +3,7 @@ package com.android.server.locksettings.recoverablekeystore; import static com.google.common.truth.Truth.assertThat; import android.security.keystore.recovery.TrustedRootCertificates; +import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -64,10 +65,10 @@ public class TestOnlyInsecureCertificateHelperTest { @Test public void testKeepOnlyWhitelistedInsecureKeys_emptyKeysList() throws Exception { - Map<String, SecretKey> rawKeys = new HashMap<>(); - Map<String, SecretKey> expectedResult = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>(); - Map<String, SecretKey> filteredKeys = + Map<String, Pair<SecretKey, byte[]>> filteredKeys = mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet()); assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet()); @@ -75,13 +76,13 @@ public class TestOnlyInsecureCertificateHelperTest { @Test public void testKeepOnlyWhitelistedInsecureKeys_singleNonWhitelistedKey() throws Exception { - Map<String, SecretKey> rawKeys = new HashMap<>(); - Map<String, SecretKey> expectedResult = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>(); String alias = "secureAlias"; - rawKeys.put(alias, TestData.generateKey()); + rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null)); - Map<String, SecretKey> filteredKeys = + Map<String, Pair<SecretKey, byte[]>> filteredKeys = mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet()); assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet()); @@ -89,14 +90,14 @@ public class TestOnlyInsecureCertificateHelperTest { @Test public void testKeepOnlyWhitelistedInsecureKeys_singleWhitelistedKey() throws Exception { - Map<String, SecretKey> rawKeys = new HashMap<>(); - Map<String, SecretKey> expectedResult = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>(); String alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX; - rawKeys.put(alias, TestData.generateKey()); + rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null)); expectedResult.put(alias, rawKeys.get(alias)); - Map<String, SecretKey> filteredKeys = + Map<String, Pair<SecretKey, byte[]>> filteredKeys = mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet()); assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet()); @@ -104,21 +105,21 @@ public class TestOnlyInsecureCertificateHelperTest { @Test public void testKeepOnlyWhitelistedInsecureKeys() throws Exception { - Map<String, SecretKey> rawKeys = new HashMap<>(); - Map<String, SecretKey> expectedResult = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>(); + Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>(); String alias = "SECURE_ALIAS" + TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX; - rawKeys.put(alias, TestData.generateKey()); + rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null)); alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "1"; - rawKeys.put(alias, TestData.generateKey()); + rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null)); expectedResult.put(alias, rawKeys.get(alias)); alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "2"; - rawKeys.put(alias, TestData.generateKey()); + rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null)); expectedResult.put(alias, rawKeys.get(alias)); - Map<String, SecretKey> filteredKeys = + Map<String, Pair<SecretKey, byte[]>> filteredKeys = mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet()); assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet()); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java index b5ee60e7ec24..9813ab74721e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.fail; import android.security.keystore.AndroidKeyStoreSecretKey; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; +import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -32,6 +33,7 @@ import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; +import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.util.HashMap; import java.util.Map; @@ -47,26 +49,49 @@ public class WrappedKeyTest { private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private static final String KEY_ALGORITHM = "AES"; private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding"; - private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias"; + private static final String WRAPPED_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias"; private static final int GENERATION_ID = 1; private static final int GCM_TAG_LENGTH_BYTES = 16; private static final int BITS_PER_BYTE = 8; private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE; + private static final byte[] NULL_METADATA = null; + private static final byte[] NON_NULL_METADATA = "keyMetadata".getBytes(StandardCharsets.UTF_8); @After public void tearDown() throws Exception { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); keyStore.load(/*param=*/ null); - keyStore.deleteEntry(WRAPPING_KEY_ALIAS); + keyStore.deleteEntry(WRAPPED_KEY_ALIAS); + } + + // TODO: Add tests for non-null metadata + + @Test + public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped_nullMetadata() throws Exception { + PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey( + GENERATION_ID, generateAndroidKeyStoreKey()); + SecretKey rawKey = generateKey(); + + WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NULL_METADATA); + + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + cipher.init( + Cipher.UNWRAP_MODE, + wrappingKey.getKey(), + new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce())); + SecretKey unwrappedKey = (SecretKey) cipher.unwrap( + wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY); + assertEquals(rawKey, unwrappedKey); } @Test - public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception { + public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped_nonNullMetadata() + throws Exception { PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey( GENERATION_ID, generateAndroidKeyStoreKey()); SecretKey rawKey = generateKey(); - WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NON_NULL_METADATA); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init( @@ -84,27 +109,47 @@ public class WrappedKeyTest { GENERATION_ID, generateAndroidKeyStoreKey()); SecretKey rawKey = generateKey(); - WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NULL_METADATA); assertEquals(GENERATION_ID, wrappedKey.getPlatformKeyGenerationId()); } @Test - public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception { + public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception { String alias = "karlin"; AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey(); SecretKey appKey = generateKey(); WrappedKey wrappedKey = WrappedKey.fromSecretKey( - new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey); + new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA); HashMap<String, WrappedKey> keysByAlias = new HashMap<>(); keysByAlias.put(alias, wrappedKey); - Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys( + Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys( new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias); assertEquals(1, unwrappedKeys.size()); assertTrue(unwrappedKeys.containsKey(alias)); - assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded()); + assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).first.getEncoded()); + assertArrayEquals(null, unwrappedKeys.get(alias).second); + } + + @Test + public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception { + String alias = "karlin"; + AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey(); + SecretKey appKey = generateKey(); + WrappedKey wrappedKey = WrappedKey.fromSecretKey( + new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA); + HashMap<String, WrappedKey> keysByAlias = new HashMap<>(); + keysByAlias.put(alias, wrappedKey); + + Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys( + new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias); + + assertEquals(1, unwrappedKeys.size()); + assertTrue(unwrappedKeys.containsKey(alias)); + assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).first.getEncoded()); + assertArrayEquals(NON_NULL_METADATA, unwrappedKeys.get(alias).second); } @Test @@ -113,11 +158,11 @@ public class WrappedKeyTest { AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey(); SecretKey appKey = generateKey(); WrappedKey wrappedKey = WrappedKey.fromSecretKey( - new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey); + new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA); HashMap<String, WrappedKey> keysByAlias = new HashMap<>(); keysByAlias.put(alias, wrappedKey); - Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys( + Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys( new PlatformDecryptionKey(GENERATION_ID, generateAndroidKeyStoreKey()), keysByAlias); @@ -128,7 +173,8 @@ public class WrappedKeyTest { public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception { AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey(); WrappedKey wrappedKey = WrappedKey.fromSecretKey( - new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey()); + new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(), + /*metadata=*/ null); HashMap<String, WrappedKey> keysByAlias = new HashMap<>(); keysByAlias.put("benji", wrappedKey); @@ -156,19 +202,11 @@ public class WrappedKeyTest { KEY_ALGORITHM, ANDROID_KEY_STORE_PROVIDER); keyGenerator.init(new KeyGenParameterSpec.Builder( - WRAPPING_KEY_ALIAS, + WRAPPED_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); return (AndroidKeyStoreSecretKey) keyGenerator.generateKey(); } - - private PlatformDecryptionKey generatePlatformDecryptionKey() throws Exception { - return generatePlatformDecryptionKey(GENERATION_ID); - } - - private PlatformDecryptionKey generatePlatformDecryptionKey(int generationId) throws Exception { - return new PlatformDecryptionKey(generationId, generateAndroidKeyStoreKey()); - } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java index 880255d79eb7..9c03df8e4369 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java @@ -55,12 +55,15 @@ public class KeyChainSnapshotSerializerTest { private static final String TEST_KEY_1_ALIAS = "key1"; private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 }; + private static final byte[] TEST_KEY_1_METADATA = new byte[] { 89, 87 }; private static final String TEST_KEY_2_ALIAS = "key2"; private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 }; + private static final byte[] TEST_KEY_2_METADATA = new byte[] {}; private static final String TEST_KEY_3_ALIAS = "key3"; private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 }; + private static final byte[] TEST_KEY_3_METADATA = new byte[] { 121 }; @Test public void roundTrip_persistsCounterId() throws Exception { @@ -144,6 +147,17 @@ public class KeyChainSnapshotSerializerTest { } @Test + public void roundTripKeys_0_persistsKeyMetadata_absent() throws Exception { + assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(0).getMetadata()).isEqualTo(null); + } + + @Test + public void roundTripKeys_0_persistsKeyMetadata_present() throws Exception { + assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(0).getMetadata()) + .isEqualTo(TEST_KEY_1_METADATA); + } + + @Test public void roundTripKeys_1_persistsAlias() throws Exception { assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS); } @@ -154,6 +168,17 @@ public class KeyChainSnapshotSerializerTest { } @Test + public void roundTripKeys_1_persistsKeyMetadata_absent() throws Exception { + assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(1).getMetadata()).isEqualTo(null); + } + + @Test + public void roundTripKeys_1_persistsKeyMetadata_present() throws Exception { + assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(1).getMetadata()) + .isEqualTo(TEST_KEY_2_METADATA); + } + + @Test public void roundTripKeys_2_persistsAlias() throws Exception { assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS); } @@ -164,28 +189,74 @@ public class KeyChainSnapshotSerializerTest { } @Test - public void serialize_doesNotThrowForTestSnapshot() throws Exception { + public void roundTripKeys_2_persistsKeyMetadata_absent() throws Exception { + assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(2).getMetadata()).isEqualTo(null); + } + + @Test + public void roundTripKeys_2_persistsKeyMetadata_present() throws Exception { + assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(2).getMetadata()) + .isEqualTo(TEST_KEY_3_METADATA); + } + + @Test + public void serialize_doesNotThrowForTestSnapshotWithoutKeyMetadata() throws Exception { KeyChainSnapshotSerializer.serialize( - createTestKeyChainSnapshot(), new ByteArrayOutputStream()); + createTestKeyChainSnapshot(/*withKeyMetadata=*/ false), + new ByteArrayOutputStream()); + } + + @Test + public void serialize_doesNotThrowForTestSnapshotWithKeyMetadata() throws Exception { + KeyChainSnapshotSerializer.serialize( + createTestKeyChainSnapshotWithKeyMetadata(), new ByteArrayOutputStream()); } private static List<WrappedApplicationKey> roundTripKeys() throws Exception { return roundTrip().getWrappedApplicationKeys(); } + private static List<WrappedApplicationKey> roundTripKeys(boolean withKeyMetadata) + throws Exception { + return roundTrip(withKeyMetadata).getWrappedApplicationKeys(); + } + private static KeyChainProtectionParams roundTripParams() throws Exception { - return roundTrip().getKeyChainProtectionParams().get(0); + return roundTrip(/*withKeyMetadata=*/ false).getKeyChainProtectionParams().get(0); } public static KeyChainSnapshot roundTrip() throws Exception { - KeyChainSnapshot snapshot = createTestKeyChainSnapshot(); + return roundTrip(/*withKeyMetadata=*/ false); + } + + public static KeyChainSnapshot roundTrip(boolean withKeyMetadata) throws Exception { + KeyChainSnapshot snapshot = createTestKeyChainSnapshot(withKeyMetadata); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream); return KeyChainSnapshotDeserializer.deserialize( new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); } - private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception { + private static KeyChainSnapshot createTestKeyChainSnapshot(boolean withKeyMetadata) + throws Exception { + KeyChainSnapshot.Builder builder = new KeyChainSnapshot.Builder() + .setCounterId(COUNTER_ID) + .setSnapshotVersion(SNAPSHOT_VERSION) + .setServerParams(SERVER_PARAMS) + .setMaxAttempts(MAX_ATTEMPTS) + .setEncryptedRecoveryKeyBlob(KEY_BLOB) + .setKeyChainProtectionParams(createKeyChainProtectionParamsList()) + .setTrustedHardwareCertPath(CERT_PATH); + if (withKeyMetadata) { + builder.setWrappedApplicationKeys(createKeysWithMetadata()); + } else { + builder.setWrappedApplicationKeys(createKeysWithoutMetadata()); + } + return builder.build(); + } + + private static KeyChainSnapshot createTestKeyChainSnapshotWithKeyMetadata() + throws Exception { return new KeyChainSnapshot.Builder() .setCounterId(COUNTER_ID) .setSnapshotVersion(SNAPSHOT_VERSION) @@ -193,16 +264,24 @@ public class KeyChainSnapshotSerializerTest { .setMaxAttempts(MAX_ATTEMPTS) .setEncryptedRecoveryKeyBlob(KEY_BLOB) .setKeyChainProtectionParams(createKeyChainProtectionParamsList()) - .setWrappedApplicationKeys(createKeys()) + .setWrappedApplicationKeys(createKeysWithMetadata()) .setTrustedHardwareCertPath(CERT_PATH) .build(); } - private static List<WrappedApplicationKey> createKeys() { + private static List<WrappedApplicationKey> createKeysWithoutMetadata() { ArrayList<WrappedApplicationKey> keyList = new ArrayList<>(); - keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES)); - keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES)); - keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES)); + keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES, /*metadata=*/ null)); + keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES, /*metadata=*/ null)); + keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES, /*metadata=*/ null)); + return keyList; + } + + private static List<WrappedApplicationKey> createKeysWithMetadata() { + ArrayList<WrappedApplicationKey> keyList = new ArrayList<>(); + keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES, TEST_KEY_1_METADATA)); + keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES, TEST_KEY_2_METADATA)); + keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES, TEST_KEY_3_METADATA)); return keyList; } @@ -221,10 +300,13 @@ public class KeyChainSnapshotSerializerTest { return keyChainProtectionParamsList; } - private static WrappedApplicationKey createKey(String alias, byte[] bytes) { - return new WrappedApplicationKey.Builder() + private static WrappedApplicationKey createKey(String alias, byte[] bytes, byte[] metadata) { + WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder() .setAlias(alias) - .setEncryptedKeyMaterial(bytes) - .build(); + .setEncryptedKeyMaterial(bytes); + if (metadata != null) { + builder.setMetadata(metadata); + } + return builder.build(); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java index 7130b4226ef1..35215c34d8f0 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java @@ -60,6 +60,7 @@ public class RecoverableKeyStoreDbHelperTest { private static final String TEST_ROOT_ALIAS = "root_cert_alias"; private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8); private static final long TEST_CERT_SERIAL = 1000L; + private static final byte[] TEST_KEY_METADATA = "test-key-metadata".getBytes(UTF_8); private static final String SQL_CREATE_V2_TABLE_KEYS = "CREATE TABLE " + KeysEntry.TABLE_NAME + "( " @@ -120,14 +121,14 @@ public class RecoverableKeyStoreDbHelperTest { @Test public void onCreate() throws Exception { mDatabaseHelper.onCreate(mDatabase); - checkAllColumns(); + checkAllColumns_latest(); } @Test public void onUpgrade_beforeV2() throws Exception { mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1, RecoverableKeyStoreDbHelper.DATABASE_VERSION); - checkAllColumns(); + checkAllColumns_latest(); } @Test @@ -135,11 +136,11 @@ public class RecoverableKeyStoreDbHelperTest { createV2Tables(); mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, RecoverableKeyStoreDbHelper.DATABASE_VERSION); - checkAllColumns(); + checkAllColumns_latest(); } @Test - public void onUpgrade_v2_to_v3_to_v4() throws Exception { + public void onUpgrade_v2_to_v3_to_v4_to_latest() throws Exception { createV2Tables(); assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table; @@ -148,9 +149,12 @@ public class RecoverableKeyStoreDbHelperTest { assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table; - mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, + mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, /*newVersion=*/ 4); + checkAllColumns_v4(); + + mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4, RecoverableKeyStoreDbHelper.DATABASE_VERSION); - checkAllColumns(); + checkAllColumns_latest(); } private boolean isRootOfTrustTableAvailable() { @@ -160,11 +164,11 @@ public class RecoverableKeyStoreDbHelperTest { values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS); values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH); values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL); - return mDatabase.insert(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values) + return mDatabase.replace(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values) > -1; } - private void checkAllColumns() throws Exception { + private void checkAllColumns_v4() throws Exception { // Check the table containing encrypted application keys ContentValues values = new ContentValues(); values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); @@ -175,7 +179,7 @@ public class RecoverableKeyStoreDbHelperTest { values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID); values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT); values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS); - assertThat(mDatabase.insert(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) + assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) .isGreaterThan(-1L); // Check the table about user metadata @@ -183,7 +187,8 @@ public class RecoverableKeyStoreDbHelperTest { values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, TEST_PLATFORM_KEY_GENERATION_ID); - assertThat(mDatabase.insert(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) + assertThat( + mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) .isGreaterThan(-1L); // Check the table about recovery service metadata @@ -202,11 +207,32 @@ public class RecoverableKeyStoreDbHelperTest { values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH); values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL); assertThat( - mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, + mDatabase.replace(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) .isGreaterThan(-1L); // Check the table about recovery service and root of trust data introduced in V4 assertThat(isRootOfTrustTableAvailable()).isTrue(); } + + private void checkAllColumns_latest() throws Exception { + // Check all columns of the previous version first. + checkAllColumns_v4(); + + ContentValues values = new ContentValues(); + values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); + values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID); + values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS); + values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE); + values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY); + values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID); + values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT); + values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS); + + // This column is added when upgrading from v4 to v5 + values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, TEST_KEY_METADATA); + + assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) + .isGreaterThan(-1L); + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java index 6a26f8c72e81..7de9ffc7f3cc 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java @@ -80,25 +80,33 @@ public class RecoverableKeyStoreDbTest { public void insertKey_replacesOldKey() { int userId = 12; int uid = 10009; - String alias = "test"; - WrappedKey oldWrappedKey = new WrappedKey( - getUtf8Bytes("nonce1"), - getUtf8Bytes("keymaterial1"), - /*platformKeyGenerationId=*/ 1); - mRecoverableKeyStoreDb.insertKey( - userId, uid, alias, oldWrappedKey); - byte[] nonce = getUtf8Bytes("nonce2"); - byte[] keyMaterial = getUtf8Bytes("keymaterial2"); - WrappedKey newWrappedKey = new WrappedKey( - nonce, keyMaterial, /*platformKeyGenerationId=*/2); + String alias = "test-alias"; - mRecoverableKeyStoreDb.insertKey( - userId, uid, alias, newWrappedKey); + byte[] nonce = getUtf8Bytes("nonce1"); + byte[] keyMaterial = getUtf8Bytes("keymaterial1"); + byte[] keyMetadata = null; + int generationId = 1; + WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); assertArrayEquals(nonce, retrievedKey.getNonce()); assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial()); - assertEquals(2, retrievedKey.getPlatformKeyGenerationId()); + assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata()); + assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId()); + + nonce = getUtf8Bytes("nonce2"); + keyMaterial = getUtf8Bytes("keymaterial2"); + keyMetadata = getUtf8Bytes("keymetadata2"); + generationId = 2; + wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); + + retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); + assertArrayEquals(nonce, retrievedKey.getNonce()); + assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial()); + assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata()); + assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId()); } @Test @@ -108,10 +116,12 @@ public class RecoverableKeyStoreDbTest { WrappedKey key1 = new WrappedKey( getUtf8Bytes("nonce1"), getUtf8Bytes("key1"), + /*metadata=*/ null, /*platformKeyGenerationId=*/ 1); WrappedKey key2 = new WrappedKey( getUtf8Bytes("nonce2"), getUtf8Bytes("key2"), + /*metadata=*/ null, /*platformKeyGenerationId=*/ 1); mRecoverableKeyStoreDb.insertKey(userId, /*uid=*/ 1, alias, key1); @@ -133,6 +143,7 @@ public class RecoverableKeyStoreDbTest { WrappedKey key = new WrappedKey( getUtf8Bytes("nonce1"), getUtf8Bytes("key1"), + /*metadata=*/ null, /*platformKeyGenerationId=*/ 1); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, key); @@ -158,13 +169,16 @@ public class RecoverableKeyStoreDbTest { String alias = "test"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); - WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, 120); + byte[] keyMetadata = getUtf8Bytes("keymetametametadata"); + + WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, 120); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); assertArrayEquals(nonce, retrievedKey.getNonce()); assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial()); + assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata()); assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId()); assertEquals(status,retrievedKey.getRecoveryStatus()); } @@ -174,20 +188,37 @@ public class RecoverableKeyStoreDbTest { int userId = 12; int uid = 1009; int generationId = 6; - String alias = "test"; - byte[] nonce = getUtf8Bytes("nonce"); - byte[] keyMaterial = getUtf8Bytes("keymaterial"); - WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId); - mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); + + String alias1 = "alias1"; + byte[] nonce1 = getUtf8Bytes("nonce1"); + byte[] keyMaterial1 = getUtf8Bytes("keymaterial1"); + byte[] keyMetadata1 = getUtf8Bytes("keyallmetadata1"); + WrappedKey wrappedKey1 = new WrappedKey(nonce1, keyMaterial1, keyMetadata1, generationId); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias1, wrappedKey1); + + String alias2 = "alias2"; + byte[] nonce2 = getUtf8Bytes("nonce2"); + byte[] keyMaterial2 = getUtf8Bytes("keymaterial2"); + byte[] keyMetadata2 = null; + WrappedKey wrappedKey2 = new WrappedKey(nonce2, keyMaterial2, keyMetadata2, generationId); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey2); Map<String, WrappedKey> keys = mRecoverableKeyStoreDb.getAllKeys(userId, uid, generationId); + assertEquals(2, keys.size()); - assertEquals(1, keys.size()); - assertTrue(keys.containsKey(alias)); - WrappedKey retrievedKey = keys.get(alias); - assertArrayEquals(nonce, retrievedKey.getNonce()); - assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial()); - assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId()); + assertTrue(keys.containsKey(alias1)); + WrappedKey retrievedKey1 = keys.get(alias1); + assertArrayEquals(nonce1, retrievedKey1.getNonce()); + assertArrayEquals(keyMaterial1, retrievedKey1.getKeyMaterial()); + assertArrayEquals(keyMetadata1, retrievedKey1.getKeyMetadata()); + assertEquals(generationId, retrievedKey1.getPlatformKeyGenerationId()); + + assertTrue(keys.containsKey(alias2)); + WrappedKey retrievedKey2 = keys.get(alias2); + assertArrayEquals(nonce2, retrievedKey2.getNonce()); + assertArrayEquals(keyMaterial2, retrievedKey2.getKeyMaterial()); + assertArrayEquals(keyMetadata2, retrievedKey2.getKeyMetadata()); + assertEquals(generationId, retrievedKey2.getPlatformKeyGenerationId()); } @Test @@ -197,6 +228,7 @@ public class RecoverableKeyStoreDbTest { WrappedKey wrappedKey = new WrappedKey( getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), + /*metadata=*/ null, /*platformKeyGenerationId=*/ 5); mRecoverableKeyStoreDb.insertKey( userId, uid, /*alias=*/ "test", wrappedKey); @@ -212,7 +244,8 @@ public class RecoverableKeyStoreDbTest { int generationId = 12; int uid = 10009; WrappedKey wrappedKey = new WrappedKey( - getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), generationId); + getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), /*metadata=*/ null, + generationId); mRecoverableKeyStoreDb.insertKey( /*userId=*/ 1, uid, /*alias=*/ "test", wrappedKey); @@ -255,7 +288,10 @@ public class RecoverableKeyStoreDbTest { String alias = "test"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); - WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status); + byte[] keyMetadata = null; + + WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, + status); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); @@ -279,12 +315,16 @@ public class RecoverableKeyStoreDbTest { String alias3 = "test3"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); + byte[] keyMetadata = null; - WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status); + WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, + status); mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey); - WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, generationId, status); - mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey); - WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, generationId); + WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, + status); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey2); + WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, keyMetadata, + generationId); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKeyWithDefaultStatus); Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid); @@ -333,7 +373,10 @@ public class RecoverableKeyStoreDbTest { String alias = "test"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); - WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status); + byte[] keyMetadata = null; + + WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, + status); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); @@ -357,7 +400,10 @@ public class RecoverableKeyStoreDbTest { String alias = "test"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); - WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status); + byte[] keyMetadata = null; + + WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, + status); mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); 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/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 9de0f903b108..319ffed3778c 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; @@ -40,6 +42,7 @@ 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; @@ -70,7 +73,6 @@ public class ActivityRecordTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - setupActivityTaskManagerService(); mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build(); mTask = mStack.getChildAt(0); mActivity = mTask.getTopActivity(); @@ -326,4 +328,44 @@ public class ActivityRecordTests extends ActivityTestsBase { 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 fda23e9cb73a..35c1edeace2d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -75,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 */)); 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 898d107d0c60..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); 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 906236d815a6..ea8f33f0c630 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -112,6 +112,10 @@ class ActivityTestsBase { @Before public void setUpBase() { mTestInjector.setUp(); + + mService = new TestActivityTaskManagerService(mContext); + mSupervisor = mService.mStackSupervisor; + mRootActivityContainer = mService.mRootActivityContainer; } @After @@ -123,17 +127,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++); 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 0c6e632ac9f3..123de2d227bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -60,7 +60,7 @@ public class AppTransitionTests extends WindowTestsBase { @BeforeClass public static void setUpRootWindowContainerMock() { - final WindowManagerService wm = WmServiceUtils.getWindowManagerService(); + 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; @@ -74,7 +74,7 @@ public class AppTransitionTests extends WindowTestsBase { @AfterClass public static void tearDownRootWindowContainerMock() { - final WindowManagerService wm = WmServiceUtils.getWindowManagerService(); + final WindowManagerService wm = TestSystemServices.getWindowManagerService(); wm.mRoot = sOriginalRootWindowContainer; sOriginalRootWindowContainer = null; } 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 a3f535aa6357..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 */); } 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/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 cdb578d23725..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*/); } @@ -276,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/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/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 89c1551dffaf..e38118e875e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -101,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 @@ -120,7 +120,7 @@ class WindowTestsBase { final Context context = getInstrumentation().getTargetContext(); - mWm = WmServiceUtils.getWindowManagerService(); + mWm = TestSystemServices.getWindowManagerService(); beforeCreateDisplay(); context.getDisplay().getDisplayInfo(mDisplayInfo); @@ -198,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; @@ -219,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( diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index c115a4bd2a10..15abdb78b214 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2019,6 +2019,12 @@ public final class Telephony { /** * Helper functions for the "threads" table used by MMS and SMS. + * + * Thread IDs are determined by the participants in a conversation and can be used to match + * both SMS and MMS messages. + * + * To avoid issues where applications might cache a thread ID, the thread ID of a deleted thread + * must not be reused to point at a new thread. */ public static final class Threads implements ThreadsColumns { @@ -2072,14 +2078,10 @@ public final class Telephony { } /** - * Given the recipients list and subject of an unsaved message, - * return its thread ID. If the message starts a new thread, - * allocate a new thread ID. Otherwise, use the appropriate - * existing thread ID. - * - * <p>Find the thread ID of the same set of recipients (in any order, - * without any additions). If one is found, return it. Otherwise, - * return a unique thread ID.</p> + * Given a set of recipients return its thread ID. + * <p> + * If a thread exists containing the provided participants, return its thread ID. Otherwise, + * this will create a new thread containing the provided participants and return its ID. */ public static long getOrCreateThreadId( Context context, Set<String> recipients) { diff --git a/core/java/android/hardware/location/ActivityChangedEvent.aidl b/telephony/java/android/telephony/CarrierRestrictionRules.aidl index 21f24459ac85..8b1f0b918901 100644 --- a/core/java/android/hardware/location/ActivityChangedEvent.aidl +++ b/telephony/java/android/telephony/CarrierRestrictionRules.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 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,9 +11,10 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 android.hardware.location; +/** @hide */ +package android.telephony; -parcelable ActivityChangedEvent;
\ No newline at end of file +parcelable CarrierRestrictionRules; diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java new file mode 100644 index 000000000000..37847aef9167 --- /dev/null +++ b/telephony/java/android/telephony/CarrierRestrictionRules.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.carrier.CarrierIdentifier; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Contains the list of carrier restrictions. + * Allowed list: it indicates the list of carriers that are allowed. + * Excluded list: it indicates the list of carriers that are excluded. + * Default carrier restriction: it indicates the default behavior and the priority between the two + * lists: + * - not allowed: the device only allows usage of carriers that are present in the allowed list + * and not present in the excluded list. This implies that if a carrier is not present in either + * list, it is not allowed. + * - allowed: the device allows all carriers, except those present in the excluded list and not + * present in the allowed list. This implies that if a carrier is not present in either list, + * it is allowed. + * MultiSim policy: it indicates the behavior in case of devices with two or more SIM cards. + * - MULTISIM_POLICY_NONE: the same configuration is applied to all SIM slots independently. This + * is the default value if none is set. + * - MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT: it indicates that any SIM card can be used + * as far as one SIM card matching the configuration is present in the device. + * + * Both lists support the character '?' as wild character. For example, an entry indicating + * MCC=310 and MNC=??? will match all networks with MCC=310. + * + * Example 1: Allowed list contains MCC and MNC of operator A. Excluded list contains operator B, + * which has same MCC and MNC, but also GID1 value. The priority allowed list is set + * to true. Only SIM cards of operator A are allowed, but not those of B or any other + * operator. + * Example 2: Allowed list contains MCC and MNC of operator A. Excluded list contains an entry + * with same MCC, and '???' as MNC. The priority allowed list is set to false. + * SIM cards of operator A and all SIM cards with a different MCC value are allowed. + * SIM cards of operators with same MCC value and different MNC are not allowed. + * @hide + */ +@SystemApi +public final class CarrierRestrictionRules implements Parcelable { + /** + * The device only allows usage of carriers that are present in the allowed list and not + * present in the excluded list. This implies that if a carrier is not present in either list, + * it is not allowed. + */ + public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; + + /** + * The device allows all carriers, except those present in the excluded list and not present + * in the allowed list. This implies that if a carrier is not present in either list, it is + * allowed. + */ + public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; + + /** The same configuration is applied to all SIM slots independently. */ + public static final int MULTISIM_POLICY_NONE = 0; + + /** Any SIM card can be used as far as one SIM card matching the configuration is present. */ + public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "MULTISIM_POLICY_", + value = {MULTISIM_POLICY_NONE, MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT}) + public @interface MultiSimPolicy {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CARRIER_RESTRICTION_DEFAULT_", + value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED}) + public @interface CarrierRestrictionDefault {} + + private List<CarrierIdentifier> mAllowedCarriers; + private List<CarrierIdentifier> mExcludedCarriers; + @CarrierRestrictionDefault + private int mCarrierRestrictionDefault; + @MultiSimPolicy + private int mMultiSimPolicy; + + private CarrierRestrictionRules() { + mAllowedCarriers = new ArrayList<CarrierIdentifier>(); + mExcludedCarriers = new ArrayList<CarrierIdentifier>(); + mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED; + mMultiSimPolicy = MULTISIM_POLICY_NONE; + } + + private CarrierRestrictionRules(Parcel in) { + mAllowedCarriers = new ArrayList<CarrierIdentifier>(); + mExcludedCarriers = new ArrayList<CarrierIdentifier>(); + + in.readTypedList(mAllowedCarriers, CarrierIdentifier.CREATOR); + in.readTypedList(mExcludedCarriers, CarrierIdentifier.CREATOR); + mCarrierRestrictionDefault = in.readInt(); + mMultiSimPolicy = in.readInt(); + } + + /** + * Creates a new builder for this class + * @hide + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Indicates if all carriers are allowed + */ + public boolean isAllCarriersAllowed() { + return (mAllowedCarriers.isEmpty() && mExcludedCarriers.isEmpty() + && mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_ALLOWED); + } + + /** + * Retrieves list of allowed carriers + * + * @return the list of allowed carriers + */ + public @NonNull List<CarrierIdentifier> getAllowedCarriers() { + return mAllowedCarriers; + } + + /** + * Retrieves list of excluded carriers + * + * @return the list of excluded carriers + */ + public @NonNull List<CarrierIdentifier> getExcludedCarriers() { + return mExcludedCarriers; + } + + /** + * Retrieves the default behavior of carrier restrictions + */ + public @CarrierRestrictionDefault int getDefaultCarrierRestriction() { + return mCarrierRestrictionDefault; + } + + /** + * @return The policy used for multi-SIM devices + */ + public @MultiSimPolicy int getMultiSimPolicy() { + return mMultiSimPolicy; + } + + /** + * {@link Parcelable#writeToParcel} + */ + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeTypedList(mAllowedCarriers); + out.writeTypedList(mExcludedCarriers); + out.writeInt(mCarrierRestrictionDefault); + out.writeInt(mMultiSimPolicy); + } + + /** + * {@link Parcelable#describeContents} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * {@link Parcelable.Creator} + */ + public static final Creator<CarrierRestrictionRules> CREATOR = + new Creator<CarrierRestrictionRules>() { + @Override + public CarrierRestrictionRules createFromParcel(Parcel in) { + return new CarrierRestrictionRules(in); + } + + @Override + public CarrierRestrictionRules[] newArray(int size) { + return new CarrierRestrictionRules[size]; + } + }; + + @Override + public String toString() { + return "CarrierRestrictionRules(allowed:" + mAllowedCarriers + ", excluded:" + + mExcludedCarriers + ", default:" + mCarrierRestrictionDefault + + ", multisim policy:" + mMultiSimPolicy + ")"; + } + + /** + * Builder for a {@link CarrierRestrictionRules}. + */ + public static class Builder { + private final CarrierRestrictionRules mRules; + + /** {@hide} */ + public Builder() { + mRules = new CarrierRestrictionRules(); + } + + /** build command */ + public CarrierRestrictionRules build() { + return mRules; + } + + /** + * Indicate that all carriers are allowed. + */ + public Builder setAllCarriersAllowed() { + mRules.mAllowedCarriers.clear(); + mRules.mExcludedCarriers.clear(); + mRules.mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_ALLOWED; + return this; + } + + /** + * Set list of allowed carriers. + * + * @param allowedCarriers list of allowed carriers + */ + public Builder setAllowedCarriers(List<CarrierIdentifier> allowedCarriers) { + mRules.mAllowedCarriers = new ArrayList<CarrierIdentifier>(allowedCarriers); + return this; + } + + /** + * Set list of excluded carriers. + * + * @param excludedCarriers list of excluded carriers + */ + public Builder setExcludedCarriers(List<CarrierIdentifier> excludedCarriers) { + mRules.mExcludedCarriers = new ArrayList<CarrierIdentifier>(excludedCarriers); + return this; + } + + /** + * Set the default behavior of the carrier restrictions + * + * @param carrierRestrictionDefault prioritized carrier list + */ + public Builder setDefaultCarrierRestriction( + @CarrierRestrictionDefault int carrierRestrictionDefault) { + mRules.mCarrierRestrictionDefault = carrierRestrictionDefault; + return this; + } + + /** + * Set the policy to be used for multi-SIM devices + * + * @param multiSimPolicy multi SIM policy + */ + public Builder setMultiSimPolicy(@MultiSimPolicy int multiSimPolicy) { + mRules.mMultiSimPolicy = multiSimPolicy; + return this; + } + } +} 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/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index e77042d1930f..ad3ca6d129c9 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -86,11 +86,6 @@ public class SignalStrength implements Parcelable { CellSignalStrengthTdscdma mTdscdma; CellSignalStrengthLte mLte; - /** Parameters from the framework */ - @UnsupportedAppUsage - private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating - // signal strength level - /** * Create a new SignalStrength from a intent notifier Bundle * @@ -140,7 +135,6 @@ public class SignalStrength implements Parcelable { mWcdma = wcdma; mTdscdma = tdscdma; mLte = lte; - mLteRsrpBoost = 0; } /** @@ -211,7 +205,6 @@ public class SignalStrength implements Parcelable { /** @hide */ public void updateLevel(PersistableBundle cc, ServiceState ss) { - mLteRsrpBoost = ss.getLteEarfcnRsrpBoost(); mCdma.updateLevel(cc, ss); mGsm.updateLevel(cc, ss); mWcdma.updateLevel(cc, ss); @@ -241,7 +234,6 @@ public class SignalStrength implements Parcelable { mWcdma = new CellSignalStrengthWcdma(s.mWcdma); mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma); mLte = new CellSignalStrengthLte(s.mLte); - mLteRsrpBoost = s.mLteRsrpBoost; } /** @@ -258,7 +250,6 @@ public class SignalStrength implements Parcelable { mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader()); mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader()); mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader()); - mLteRsrpBoost = in.readInt(); } /** @@ -270,8 +261,6 @@ public class SignalStrength implements Parcelable { out.writeParcelable(mWcdma, flags); out.writeParcelable(mTdscdma, flags); out.writeParcelable(mLte, flags); - - out.writeInt(mLteRsrpBoost); } /** @@ -384,11 +373,6 @@ public class SignalStrength implements Parcelable { return mLte.getCqi(); } - /** @hide */ - public int getLteRsrpBoost() { - return mLteRsrpBoost; - } - /** * Retrieve an abstract level value for the overall signal strength. * @@ -616,7 +600,7 @@ public class SignalStrength implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mLteRsrpBoost); + return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte); } /** @@ -632,8 +616,7 @@ public class SignalStrength implements Parcelable { && mGsm.equals(s.mGsm) && mWcdma.equals(s.mWcdma) && mTdscdma.equals(s.mTdscdma) - && mLte.equals(s.mLte) - && mLteRsrpBoost == s.mLteRsrpBoost; + && mLte.equals(s.mLte); } /** @@ -647,7 +630,6 @@ public class SignalStrength implements Parcelable { .append(",mWcdma=").append(mWcdma) .append(",mTdscdma=").append(mTdscdma) .append(",mLte=").append(mLte) - .append(",mLteRsrpBoost=").append(mLteRsrpBoost) .append(",primary=").append(getPrimary().getClass().getSimpleName()) .append("}") .toString(); @@ -666,8 +648,6 @@ public class SignalStrength implements Parcelable { mWcdma = m.getParcelable("Wcdma"); mTdscdma = m.getParcelable("Tdscdma"); mLte = m.getParcelable("Lte"); - - mLteRsrpBoost = m.getInt("LteRsrpBoost"); } /** @@ -683,8 +663,6 @@ public class SignalStrength implements Parcelable { m.putParcelable("Wcdma", mWcdma); m.putParcelable("Tdscdma", mTdscdma); m.putParcelable("Lte", mLte); - - m.putInt("LteRsrpBoost", mLteRsrpBoost); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3311218bdb3c..80533535017e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9055,6 +9055,8 @@ public class TelephonyManager { * <p>This method works only on devices with {@link * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. * + * @deprecated use setCarrierRestrictionRules instead + * * @return The number of carriers set successfully. Should be length of * carrierList on success; -1 if carrierList null or on error. * @hide @@ -9062,44 +9064,144 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) { + // Execute the method setCarrierRestrictionRules with an empty excluded list and + // indicating priority for the allowed list. + CarrierRestrictionRules carrierRestrictionRules = CarrierRestrictionRules.newBuilder() + .setAllowedCarriers(carriers) + .setDefaultCarrierRestriction( + CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) + .build(); + + int result = setCarrierRestrictionRules(carrierRestrictionRules); + + // Convert boolean result into int, as required by this method. + if (result == SET_CARRIER_RESTRICTION_SUCCESS) { + return carriers.size(); + } else { + return -1; + } + } + + /** + * The carrier restrictions were successfully set. + * @hide + */ + @SystemApi + public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0; + + /** + * The carrier restrictions were not set due to lack of support in the modem. This can happen + * if the modem does not support setting the carrier restrictions or if the configuration + * passed in the {@code setCarrierRestrictionRules} is not supported by the modem. + * @hide + */ + @SystemApi + public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; + + /** + * The setting of carrier restrictions failed. + * @hide + */ + @SystemApi + public static final int SET_CARRIER_RESTRICTION_ERROR = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_CARRIER_RESTRICTION_"}, + value = { + SET_CARRIER_RESTRICTION_SUCCESS, + SET_CARRIER_RESTRICTION_NOT_SUPPORTED, + SET_CARRIER_RESTRICTION_ERROR + }) + public @interface SetCarrierRestrictionResult {} + + /** + * Set the allowed carrier list and the excluded carrier list indicating the priority between + * the two lists. + * Requires system privileges. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * + * <p>This method works only on devices with {@link + * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. + * + * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success. + * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the + * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SetCarrierRestrictionResult + public int setCarrierRestrictionRules(@NonNull CarrierRestrictionRules rules) { try { ITelephony service = getITelephony(); if (service != null) { - return service.setAllowedCarriers(slotIndex, carriers); + return service.setAllowedCarriers(rules); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e); } catch (NullPointerException e) { Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e); } - return -1; + return SET_CARRIER_RESTRICTION_ERROR; } /** * Get the allowed carrier list for slotIndex. - * Require system privileges. In the future we may add this to carrier APIs. + * Requires system privileges. * * <p>This method returns valid data on devices with {@link * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. * + * @deprecated Apps should use {@link getCarriersRestrictionRules} to retrieve the list of + * allowed and excliuded carriers, as the result of this API is valid only when the excluded + * list is empty. This API could return an empty list, even if some restrictions are present. + * * @return List of {@link android.telephony.CarrierIdentifier}; empty list * means all carriers are allowed. * @hide */ + @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) { + CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules(); + if (carrierRestrictionRule != null) { + return carrierRestrictionRule.getAllowedCarriers(); + } + return new ArrayList<CarrierIdentifier>(0); + } + + /** + * Get the allowed carrier list and the excluded carrier list indicating the priority between + * the two lists. + * Require system privileges. In the future we may add this to carrier APIs. + * + * <p>This method returns valid data on devices with {@link + * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. + * + * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the + * excluded carrier list with the priority between the two lists. Returns {@code null} + * in case of error. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @Nullable + public CarrierRestrictionRules getCarrierRestrictionRules() { try { ITelephony service = getITelephony(); if (service != null) { - return service.getAllowedCarriers(slotIndex); + return service.getAllowedCarriers(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e); } catch (NullPointerException e) { Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e); } - return new ArrayList<CarrierIdentifier>(0); + return null; } /** @@ -9972,4 +10074,33 @@ public class TelephonyManager { } return ret; } + + /** + * Enable or disable a logical modem stack. When a logical modem is disabled, the corresponding + * SIM will still be visible to the user but its mapping modem will not have any radio activity. + * For example, we will disable a modem when user or system believes the corresponding SIM + * is temporarily not needed (e.g. out of coverage), and will enable it back on when needed. + * + * Requires that the calling app has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * @param slotIndex which corresponding modem will operate on. + * @param enable whether to enable or disable the modem stack. + * @return whether the operation is successful. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean enableModemForSlot(int slotIndex, boolean enable) { + boolean ret = false; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + ret = telephony.enableModemForSlot(slotIndex, enable); + } + } catch (RemoteException ex) { + Log.e(TAG, "enableModem RemoteException", ex); + } + return ret; + } } 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/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 42a788d0cc8e..0e5c71d6ef90 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -361,7 +361,7 @@ public class EuiccManager { public boolean isEnabled() { // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic // restrictions. - return getIEuiccController() != null; + return getIEuiccController() != null && mCardId != TelephonyManager.INVALID_CARD_ID; } /** 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 17516bc24aca..6e8d038eda4f 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -87,7 +87,7 @@ public class DctConstants { public static final int CMD_NET_STAT_POLL = BASE + 40; public static final int EVENT_DATA_RAT_CHANGED = BASE + 41; public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42; - public static final int EVENT_REDIRECTION_DETECTED = BASE + 44; + public static final int EVENT_NETWORK_STATUS_CHANGED = BASE + 44; public static final int EVENT_PCO_DATA_RECEIVED = BASE + 45; public static final int EVENT_DATA_ENABLED_CHANGED = BASE + 46; public static final int EVENT_DATA_RECONNECT = BASE + 47; diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index c5d82c5949f0..5736a465d449 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -28,6 +28,7 @@ import android.net.Uri; import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; +import android.telephony.CarrierRestrictionRules; import android.telephony.CellInfo; import android.telephony.ClientRequestStats; import android.telephony.IccOpenLogicalChannelResponse; @@ -41,6 +42,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; @@ -1292,22 +1294,27 @@ interface ITelephony { List<TelephonyHistogram> getTelephonyHistograms(); /** - * Set the allowed carrier list for slotIndex - * Require system privileges. In the future we may add this to carrier APIs. + * Set the allowed carrier list and the excluded carrier list, indicating the priority between + * the two lists. * - * @return The number of carriers set successfully. Should match length of - * carriers on success. + * <p>Requires system privileges. In the future we may add this to carrier APIs. + * + * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success. + * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the + * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases. */ - int setAllowedCarriers(int slotIndex, in List<CarrierIdentifier> carriers); + int setAllowedCarriers(in CarrierRestrictionRules carrierRestrictionRules); /** - * Get the allowed carrier list for slotIndex. - * Require system privileges. In the future we may add this to carrier APIs. + * Get the allowed carrier list and the excluded carrier list indicating the priority between + * the two lists. + * + * <p>Requires system privileges. In the future we may add this to carrier APIs. * - * @return List of {@link android.service.carrier.CarrierIdentifier}; empty list - * means all carriers are allowed. + * @return {@link CarrierRestrictionRules}; empty lists mean all carriers are allowed. It + * returns null in case of error. */ - List<CarrierIdentifier> getAllowedCarriers(int slotIndex); + CarrierRestrictionRules getAllowedCarriers(); /** * Returns carrier id of the given subscription. @@ -1776,4 +1783,19 @@ 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(); + + /** + * Enable or disable a logical modem stack associated with the slotIndex. + */ + boolean enableModemForSlot(int slotIndex, boolean enable); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 1c103a9fc747..930003462110 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -489,6 +489,7 @@ public interface RILConstants { int RIL_REQUEST_STOP_NETWORK_SCAN = 143; int RIL_REQUEST_START_KEEPALIVE = 144; int RIL_REQUEST_STOP_KEEPALIVE = 145; + int RIL_REQUEST_ENABLE_MODEM = 146; /* The following requests are not defined in RIL.h */ int RIL_REQUEST_HAL_NON_RIL_BASE = 200; 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/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index 77cd9d8f20a3..c2e735e184b0 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -178,10 +178,10 @@ public class RollbackTest { } /** - * Test that rollback data is properly persisted. + * Test that multiple available rollbacks are properly persisted. */ @Test - public void testRollbackDataPersistence() throws Exception { + public void testAvailableRollbackPersistence() throws Exception { try { RollbackTestUtils.adoptShellPermissionIdentity( Manifest.permission.INSTALL_PACKAGES, @@ -190,36 +190,157 @@ public class RollbackTest { RollbackManager rm = RollbackTestUtils.getRollbackManager(); - // TODO: Test this with multi-package rollback, not just single - // package rollback. - // Prep installation of TEST_APP_A RollbackTestUtils.uninstall(TEST_APP_A); RollbackTestUtils.install("RollbackTestAppAv1.apk", false); RollbackTestUtils.install("RollbackTestAppAv2.apk", true); assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + RollbackTestUtils.uninstall(TEST_APP_B); + RollbackTestUtils.install("RollbackTestAppBv1.apk", false); + RollbackTestUtils.install("RollbackTestAppBv2.apk", true); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); + + // Both test apps should now be available for rollback. + // TODO: See if there is a way to remove this race condition + // between when the app is installed and when the rollback + // is made available. + Thread.sleep(1000); + + assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); + RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A); + assertNotNull(rollbackA); + assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName); + assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode); + + assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); + RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B); + assertNotNull(rollbackB); + assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName); + assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode); + + // Reload the persisted data. + rm.reloadPersistedData(); + + // The apps should still be available for rollback. + rollbackA = rm.getAvailableRollback(TEST_APP_A); + assertNotNull(rollbackA); + assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName); + assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode); + + assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); + rollbackB = rm.getAvailableRollback(TEST_APP_B); + assertNotNull(rollbackB); + assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName); + assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode); + + // Rollback of B should not rollback A + RollbackTestUtils.rollback(rollbackB); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + + /** + * Test that available multi-package rollbacks are properly persisted. + */ + @Test + public void testAvailableMultiPackageRollbackPersistence() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.MANAGE_ROLLBACKS); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.uninstall(TEST_APP_B); + RollbackTestUtils.installMultiPackage(false, + "RollbackTestAppAv1.apk", + "RollbackTestAppBv1.apk"); + RollbackTestUtils.installMultiPackage(true, + "RollbackTestAppAv2.apk", + "RollbackTestAppBv2.apk"); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); + // The app should now be available for rollback. // TODO: See if there is a way to remove this race condition // between when the app is installed and when the rollback // is made available. Thread.sleep(1000); + assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollback); - assertEquals(TEST_APP_A, rollback.targetPackage.packageName); - assertEquals(2, rollback.targetPackage.higherVersion.versionCode); - assertEquals(1, rollback.targetPackage.lowerVersion.versionCode); + RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A); + assertNotNull(rollbackA); + assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName); + assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode); + + assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); + RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B); + assertNotNull(rollbackB); + assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName); + assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode); // Reload the persisted data. rm.reloadPersistedData(); - // The app should still be available for rollback. + // The apps should still be available for rollback. + rollbackA = rm.getAvailableRollback(TEST_APP_A); + assertNotNull(rollbackA); + assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName); + assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode); + + assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); + rollbackB = rm.getAvailableRollback(TEST_APP_B); + assertNotNull(rollbackB); + assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName); + assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode); + assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode); + + // Rollback of B should rollback A as well + RollbackTestUtils.rollback(rollbackB); + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + + /** + * Test that recently executed rollback data is properly persisted. + */ + @Test + public void testRecentlyExecutedRollbackPersistence() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.MANAGE_ROLLBACKS); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + RollbackTestUtils.install("RollbackTestAppAv2.apk", true); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + // The app should now be available for rollback. + // TODO: See if there is a way to remove this race condition + // between when the app is installed and when the rollback + // is made available. + Thread.sleep(1000); assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - rollback = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollback); - assertEquals(TEST_APP_A, rollback.targetPackage.packageName); - assertEquals(2, rollback.targetPackage.higherVersion.versionCode); - assertEquals(1, rollback.targetPackage.lowerVersion.versionCode); + RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); // Roll back the app. RollbackTestUtils.rollback(rollback); diff --git a/tests/net/Android.mk b/tests/net/Android.mk index f6f35fdadcd1..685067377166 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -54,6 +54,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libnativehelper \ libpackagelistparser \ libpcre2 \ + libprocessgroup \ libselinux \ libui \ libutils \ 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 2a92a7dabd98..882babff4aee 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -122,7 +122,6 @@ import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkUtils; import android.net.RouteInfo; -import android.net.StringNetworkSpecifier; import android.net.UidRange; import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; @@ -145,6 +144,7 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; @@ -2567,16 +2567,76 @@ public class ConnectivityServiceTest { return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); } + /** + * Verify request matching behavior with network specifiers. + * + * Note: this test is somewhat problematic since it involves removing capabilities from + * agents - i.e. agents rejecting requests which they previously accepted. This is flagged + * as a WTF bug in + * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)} but + * does work. + */ @Test public void testNetworkSpecifier() { + // A NetworkSpecifier subclass that matches all networks but must not be visible to apps. + class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements + Parcelable { + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + return true; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) {} + + @Override + public NetworkSpecifier redact() { + return null; + } + } + + // A network specifier that matches either another LocalNetworkSpecifier with the same + // string or a ConfidentialMatchAllNetworkSpecifier, and can be passed to apps as is. + class LocalStringNetworkSpecifier extends NetworkSpecifier implements Parcelable { + private String mString; + + LocalStringNetworkSpecifier(String string) { + mString = string; + } + + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + if (other instanceof LocalStringNetworkSpecifier) { + return TextUtils.equals(mString, + ((LocalStringNetworkSpecifier) other).mString); + } + if (other instanceof ConfidentialMatchAllNetworkSpecifier) return true; + return false; + } + + @Override + public int describeContents() { + return 0; + } + @Override + public void writeToParcel(Parcel dest, int flags) {} + } + + NetworkRequest rEmpty1 = newWifiRequestBuilder().build(); NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build(); NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build(); NetworkRequest rEmpty4 = newWifiRequestBuilder().setNetworkSpecifier( (NetworkSpecifier) null).build(); - NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier("foo").build(); + NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier( + new LocalStringNetworkSpecifier("foo")).build(); NetworkRequest rBar = newWifiRequestBuilder().setNetworkSpecifier( - new StringNetworkSpecifier("bar")).build(); + new LocalStringNetworkSpecifier("bar")).build(); TestNetworkCallback cEmpty1 = new TestNetworkCallback(); TestNetworkCallback cEmpty2 = new TestNetworkCallback(); @@ -2585,7 +2645,7 @@ public class ConnectivityServiceTest { TestNetworkCallback cFoo = new TestNetworkCallback(); TestNetworkCallback cBar = new TestNetworkCallback(); TestNetworkCallback[] emptyCallbacks = new TestNetworkCallback[] { - cEmpty1, cEmpty2, cEmpty3 }; + cEmpty1, cEmpty2, cEmpty3, cEmpty4 }; mCm.registerNetworkCallback(rEmpty1, cEmpty1); mCm.registerNetworkCallback(rEmpty2, cEmpty2); @@ -2594,6 +2654,9 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(rFoo, cFoo); mCm.registerNetworkCallback(rBar, cBar); + LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo"); + LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar"); + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2602,30 +2665,54 @@ public class ConnectivityServiceTest { cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertNoCallbacks(cFoo, cBar); - mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo")); + mWiFiNetworkAgent.setNetworkSpecifier(nsFoo); cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); for (TestNetworkCallback c: emptyCallbacks) { - c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent); + c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsFoo), + mWiFiNetworkAgent); } - cFoo.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent); + cFoo.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsFoo), + mWiFiNetworkAgent); + assertEquals(nsFoo, + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); cFoo.assertNoCallback(); - mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar")); + mWiFiNetworkAgent.setNetworkSpecifier(nsBar); cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); for (TestNetworkCallback c: emptyCallbacks) { - c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent); + c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsBar), + mWiFiNetworkAgent); } - cBar.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent); + cBar.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsBar), + mWiFiNetworkAgent); + assertEquals(nsBar, + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); + cBar.assertNoCallback(); + + mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier()); + cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + for (TestNetworkCallback c : emptyCallbacks) { + c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null, + mWiFiNetworkAgent); + } + cFoo.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null, + mWiFiNetworkAgent); + cBar.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null, + mWiFiNetworkAgent); + assertNull( + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); + cFoo.assertNoCallback(); cBar.assertNoCallback(); mWiFiNetworkAgent.setNetworkSpecifier(null); + cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); cBar.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); for (TestNetworkCallback c: emptyCallbacks) { c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent); } - assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cFoo, cBar); + assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); } @Test diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index d8f961850906..a844cfeeeb80 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -535,7 +535,10 @@ public class IpSecServiceParameterizedTest { IpSecTransformResponse createTransformResp = mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); int resourceId = createTransformResp.resourceId; mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId); @@ -552,7 +555,9 @@ public class IpSecServiceParameterizedTest { @Test public void testRemoveTransportModeTransform() throws Exception { - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); mIpSecService.removeTransportModeTransforms(pfd); verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 724446e11c83..5be7c7bb2040 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -422,7 +422,9 @@ public class IpSecServiceTest { @Test public void testRemoveTransportModeTransform() throws Exception { - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); mIpSecService.removeTransportModeTransforms(pfd); verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 58702dc465cc..2f8ca2d62061 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -714,7 +714,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, } } - diag_->Warn(DiagMessage(out_resource->source) + // If the resource type was not recognized, write the error and return false. + diag_->Error(DiagMessage(out_resource->source) << "unknown resource type '" << parser->element_name() << "'"); return false; } @@ -1164,8 +1165,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/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/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 52375a39b93f..92beb4eb7ce4 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -701,7 +701,10 @@ int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* } const std::string out_path = BuildIntermediateContainerFilename(path_data); - error |= !compile_func(context, options, path_data, file, output_writer, out_path); + if (!compile_func(context, options, path_data, file, output_writer, out_path)) { + context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "file failed to compile"); + error = true; + } } return error ? 1 : 0; 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/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/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp index ca6b3c4572f5..9b5df56e3987 100644 --- a/tools/processors/view_inspector/Android.bp +++ b/tools/processors/view_inspector/Android.bp @@ -5,7 +5,7 @@ java_library_host { java_resource_dirs: ["src/resources"], static_libs: [ - "javapoet", + "javapoet", ], use_tools_jar: true, @@ -18,9 +18,9 @@ java_test_host { java_resource_dirs: ["test/resources"], static_libs: [ - "guava", "junit", - "view-inspector-annotation-processor", + "guava", + "view-inspector-annotation-processor" ], test_suites: ["general-tests"], diff --git a/tools/processors/view_inspector/TEST_MAPPING b/tools/processors/view_inspector/TEST_MAPPING index a91b5b452c39..b4c9cab6916e 100644 --- a/tools/processors/view_inspector/TEST_MAPPING +++ b/tools/processors/view_inspector/TEST_MAPPING @@ -2,6 +2,8 @@ "presubmit": [ { "name": "view-inspector-annotation-processor-test" + }, { + "name": "CtsViewInspectorAnnotationProcessorTestCases" } ] } diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java index f157949f4d1b..fc4cd01a5a2a 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; @@ -78,40 +79,126 @@ final class AnnotationUtils { } /** - * Extract a string-valued property from an {@link AnnotationMirror}. + * Determine if an annotation with the supplied qualified name is present on the element. * - * @param propertyName The name of the requested property - * @param annotationMirror The mirror to search for the property - * @return The String value of the annotation or null + * @param element The element to check for the presence of an annotation + * @param annotationQualifiedName The name of the annotation to check for + * @return True if the annotation is present, false otherwise */ - Optional<String> stringProperty(String propertyName, AnnotationMirror annotationMirror) { - final AnnotationValue value = valueByName(propertyName, annotationMirror); - if (value != null) { - return Optional.of((String) value.getValue()); - } else { - return Optional.empty(); + boolean hasAnnotation(Element element, String annotationQualifiedName) { + final TypeElement namedElement = mElementUtils.getTypeElement(annotationQualifiedName); + + if (namedElement != null) { + final TypeMirror annotationType = namedElement.asType(); + + for (AnnotationMirror annotation : element.getAnnotationMirrors()) { + if (mTypeUtils.isSubtype(annotation.getAnnotationType(), annotationType)) { + return true; + } + } } + + return false; } + /** + * Get the typed value of an annotation property by name. + * + * The returned optional will be empty if the value was left at the default, or if the value + * of the property is null. + * + * @param propertyName The name of the property to search for + * @param valueClass The expected class of the property value + * @param element The element the annotation is on, used for exceptions + * @param annotationMirror An annotation mirror to search for the property + * @param <T> The type of the value + * @return An optional containing the typed value of the named property + */ + <T> Optional<T> typedValueByName( + String propertyName, + Class<T> valueClass, + Element element, + AnnotationMirror annotationMirror) { + return valueByName(propertyName, annotationMirror).map(annotationValue -> { + final Object value = annotationValue.getValue(); + + if (value == null) { + throw new ProcessingException( + String.format( + "Unexpected null value for annotation property \"%s\".", + propertyName), + element, + annotationMirror, + annotationValue); + } + + if (valueClass.isAssignableFrom(value.getClass())) { + return valueClass.cast(value); + } else { + throw new ProcessingException( + String.format( + "Expected annotation property \"%s\" to have type %s, but got %s.", + propertyName, + valueClass.getCanonicalName(), + value.getClass().getCanonicalName()), + element, + annotationMirror, + annotationValue); + } + }); + } + + /** + * Get the untyped value of an annotation property by name. + * + * The returned optional will be empty if the value was left at the default, or if the value + * of the property is null. + * + * @param propertyName The name of the property to search for + * @param element The element the annotation is on, used for exceptions + * @param annotationMirror An annotation mirror to search for the property + * @return An optional containing the untyped value of the named property + * @see AnnotationValue#getValue() + */ + Optional<Object> untypedValueByName( + String propertyName, + Element element, + AnnotationMirror annotationMirror) { + return valueByName(propertyName, annotationMirror).map(annotationValue -> { + final Object value = annotationValue.getValue(); + + if (value == null) { + throw new ProcessingException( + String.format( + "Unexpected null value for annotation property \"%s\".", + propertyName), + element, + annotationMirror, + annotationValue); + } + + return value; + }); + } /** * Extract a {@link AnnotationValue} from a mirror by string property name. * * @param propertyName The name of the property requested property - * @param annotationMirror - * @return + * @param annotationMirror The mirror to search for the property + * @return The value of the property */ - AnnotationValue valueByName(String propertyName, AnnotationMirror annotationMirror) { + Optional<AnnotationValue> valueByName(String propertyName, AnnotationMirror annotationMirror) { final Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap = annotationMirror.getElementValues(); for (ExecutableElement method : valueMap.keySet()) { if (method.getSimpleName().contentEquals(propertyName)) { - return valueMap.get(method); + return Optional.ofNullable(valueMap.get(method)); } } - return null; + // Property not explicitly defined, use default value. + return Optional.empty(); } - } diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java index 579745d2aaef..f1ebb87fed4d 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.squareup.javapoet.ClassName; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; /** @@ -70,7 +71,7 @@ public final class InspectableClassModel { * @return The property or an empty optional */ public Optional<Property> getProperty(String name) { - return Optional.of(mPropertyMap.get(name)); + return Optional.ofNullable(mPropertyMap.get(name)); } /** @@ -87,13 +88,15 @@ public final class InspectableClassModel { */ public static final class Property { private final String mName; - private String mGetter; - private Type mType; + private final String mGetter; + private final Type mType; private boolean mAttributeIdInferrableFromR = true; private int mAttributeId = 0; - public Property(String name) { - mName = name; + public Property(String name, String getter, Type type) { + mName = Objects.requireNonNull(name, "Name must not be null"); + mGetter = Objects.requireNonNull(getter, "Getter must not be null"); + mType = Objects.requireNonNull(type, "Type must not be null"); } public int getAttributeId() { @@ -126,18 +129,10 @@ public final class InspectableClassModel { return mGetter; } - public void setGetter(String getter) { - mGetter = getter; - } - public Type getType() { return mType; } - public void setType(Type type) { - mType = type; - } - public enum Type { /** Primitive or boxed {@code boolean} */ BOOLEAN, diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java index a186a82af160..46819b28c1e8 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; /** - * Process {InspectableNodeName} annotations + * Process {@code @InspectableNodeName} annotations. * * @see android.view.inspector.InspectableNodeName */ @@ -58,7 +58,8 @@ public final class InspectableNodeNameProcessor implements ModelProcessor { try { final AnnotationMirror mirror = mAnnotationUtils.exactlyOneMirror(mQualifiedName, element); - final Optional<String> nodeName = mAnnotationUtils.stringProperty("value", mirror); + final Optional<String> nodeName = mAnnotationUtils + .typedValueByName("value", String.class, element, mirror); if (!model.getNodeName().isPresent() || model.getNodeName().equals(nodeName)) { model.setNodeName(nodeName); diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java new file mode 100644 index 000000000000..f666be7a2a61 --- /dev/null +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java @@ -0,0 +1,408 @@ +/* + * 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.processor.view.inspector; + +import android.processor.view.inspector.InspectableClassModel.Property; + +import java.util.Set; +import java.util.regex.Pattern; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.NoType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; + +/** + * Process {@code @InspectableProperty} annotations. + * + * @see android.view.inspector.InspectableProperty + */ +public final class InspectablePropertyProcessor implements ModelProcessor { + private final String mQualifiedName; + private final ProcessingEnvironment mProcessingEnv; + private final AnnotationUtils mAnnotationUtils; + + /** + * Regex that matches methods names of the form {@code #getValue()}. + */ + private static final Pattern GETTER_GET_PREFIX = Pattern.compile("\\Aget[A-Z]"); + + /** + * Regex that matches method name of the form {@code #isPredicate()}. + */ + private static final Pattern GETTER_IS_PREFIX = Pattern.compile("\\Ais[A-Z]"); + + /** + * Set of android and androidx annotation qualified names for colors packed into {@code int}. + * + * @see android.annotation.ColorInt + */ + private static final String[] COLOR_INT_ANNOTATION_NAMES = { + "android.annotation.ColorInt", + "androidx.annotation.ColorInt"}; + + /** + * Set of android and androidx annotation qualified names for colors packed into {@code long}. + * @see android.annotation.ColorLong + */ + private static final String[] COLOR_LONG_ANNOTATION_NAMES = { + "android.annotation.ColorLong", + "androidx.annotation.ColorLong"}; + + /** + * @param annotationQualifiedName The qualified name of the annotation to process + * @param processingEnv The processing environment from the parent processor + */ + public InspectablePropertyProcessor( + String annotationQualifiedName, + ProcessingEnvironment processingEnv) { + mQualifiedName = annotationQualifiedName; + mProcessingEnv = processingEnv; + mAnnotationUtils = new AnnotationUtils(processingEnv); + } + + @Override + public void process(Element element, InspectableClassModel model) { + try { + final AnnotationMirror annotation = + mAnnotationUtils.exactlyOneMirror(mQualifiedName, element); + final ExecutableElement getter = ensureGetter(element); + final Property property = buildProperty(getter, annotation); + + model.getProperty(property.getName()).ifPresent(p -> { + throw new ProcessingException( + String.format( + "Property \"%s\" is already defined on #%s().", + p.getName(), + p.getGetter()), + getter, + annotation); + }); + + model.putProperty(property); + } catch (ProcessingException processingException) { + processingException.print(mProcessingEnv.getMessager()); + } + } + + /** + * Check that an element is shaped like a getter. + * + * @param element An element that hopefully represents a getter + * @throws ProcessingException if the element isn't a getter + * @return An {@link ExecutableElement} that represents a getter method. + */ + private ExecutableElement ensureGetter(Element element) { + if (element.getKind() != ElementKind.METHOD) { + throw new ProcessingException( + String.format("Expected a method, got a %s", element.getKind()), + element); + } + + final ExecutableElement method = (ExecutableElement) element; + final Set<Modifier> modifiers = method.getModifiers(); + + if (modifiers.contains(Modifier.PRIVATE)) { + throw new ProcessingException( + "Property getter methods must not be private.", + element); + } + + if (modifiers.contains(Modifier.ABSTRACT)) { + throw new ProcessingException( + "Property getter methods must not be abstract.", + element); + } + + if (modifiers.contains(Modifier.STATIC)) { + throw new ProcessingException( + "Property getter methods must not be static.", + element); + } + + if (!method.getParameters().isEmpty()) { + throw new ProcessingException( + String.format( + "Expected a getter method to take no parameters, " + + "but got %d parameters.", + method.getParameters().size()), + element); + } + + if (method.isVarArgs()) { + throw new ProcessingException( + "Expected a getter method to take no arguments, but got a var args method.", + element); + } + + if (method.getReturnType() instanceof NoType) { + throw new ProcessingException( + "Expected a getter to have a return type, got void.", + element); + } + + return method; + } + + /** + * Build a {@link Property} from a getter and an inspectable property annotation. + * + * @param getter An element representing the getter to build from + * @param annotation A mirror of an inspectable property-shaped annotation + * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled + * @return A property for the getter and annotation + */ + private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) { + final String name = mAnnotationUtils + .typedValueByName("name", String.class, getter, annotation) + .orElseGet(() -> inferPropertyNameFromGetter(getter)); + + final Property property = new Property( + name, + getter.getSimpleName().toString(), + determinePropertyType(getter, annotation)); + + mAnnotationUtils + .typedValueByName("hasAttributeId", Boolean.class, getter, annotation) + .ifPresent(property::setAttributeIdInferrableFromR); + + mAnnotationUtils + .typedValueByName("attributeId", Integer.class, getter, annotation) + .ifPresent(property::setAttributeId); + + return property; + } + + /** + * Determine the property type from the annotation, return type, or context clues. + * + * @param getter An element representing the getter to build from + * @param annotation A mirror of an inspectable property-shaped annotation + * @return The resolved property type + * @throws ProcessingException If the property type cannot be resolved + * @see android.view.inspector.InspectableProperty#valueType() + */ + private Property.Type determinePropertyType( + ExecutableElement getter, + AnnotationMirror annotation) { + + final String valueType = mAnnotationUtils + .untypedValueByName("valueType", getter, annotation) + .map(Object::toString) + .orElse("INFERRED"); + + final Property.Type returnType = convertReturnTypeToPropertyType(getter); + + switch (valueType) { + case "INFERRED": + if (hasColorAnnotation(getter)) { + return Property.Type.COLOR; + } else { + return returnType; + } + case "NONE": + return returnType; + case "COLOR": + switch (returnType) { + case COLOR: + case INT: + case LONG: + return Property.Type.COLOR; + default: + throw new ProcessingException( + "Color must be a long, integer, or android.graphics.Color", + getter, + annotation); + } + case "GRAVITY": + if (returnType == Property.Type.INT) { + return Property.Type.GRAVITY; + } else { + throw new ProcessingException( + String.format("Gravity must be an integer, got %s", returnType), + getter, + annotation); + } + case "INT_ENUM": + case "INT_FLAG": + throw new ProcessingException("Not implemented", getter, annotation); + default: + throw new ProcessingException( + String.format("Unknown value type enumeration value: %s", valueType), + getter, + annotation); + } + } + + /** + * Get a property type from the return type of a getter. + * + * @param getter The getter to extract the return type of + * @throws ProcessingException If the return type is not a primitive or an object + * @return The property type returned by the getter + */ + private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) { + final TypeMirror returnType = getter.getReturnType(); + + switch (unboxType(returnType)) { + case BOOLEAN: + return Property.Type.BOOLEAN; + case BYTE: + return Property.Type.BYTE; + case CHAR: + return Property.Type.CHAR; + case DOUBLE: + return Property.Type.DOUBLE; + case FLOAT: + return Property.Type.FLOAT; + case INT: + return Property.Type.INT; + case LONG: + return Property.Type.LONG; + case SHORT: + return Property.Type.SHORT; + case DECLARED: + if (isColorType(returnType)) { + return Property.Type.COLOR; + } else { + return Property.Type.OBJECT; + } + default: + throw new ProcessingException( + String.format("Unsupported return type %s.", returnType), + getter); + } + } + + /** + * Determine if a getter is annotated with color annotation matching its return type. + * + * Note that an {@code int} return value annotated with {@link android.annotation.ColorLong} is + * not considered to be annotated, nor is a {@code long} annotated with + * {@link android.annotation.ColorInt}. + * + * @param getter The getter to query + * @return True if the getter has a color annotation, false otherwise + * + */ + private boolean hasColorAnnotation(ExecutableElement getter) { + switch (unboxType(getter.getReturnType())) { + case INT: + for (String name : COLOR_INT_ANNOTATION_NAMES) { + if (mAnnotationUtils.hasAnnotation(getter, name)) { + return true; + } + } + return false; + case LONG: + for (String name : COLOR_LONG_ANNOTATION_NAMES) { + if (mAnnotationUtils.hasAnnotation(getter, name)) { + return true; + } + } + return false; + default: + return false; + } + } + + /** + * Infer a property name from a getter method. + * + * If the method is prefixed with {@code get}, the prefix will be stripped, and the + * capitalization fixed. E.g.: {@code getSomeProperty} to {@code someProperty}. + * + * Additionally, if the method's return type is a boolean, an {@code is} prefix will also be + * stripped. E.g.: {@code isPropertyEnabled} to {@code propertyEnabled}. + * + * Failing that, this method will just return the full name of the getter. + * + * @param getter An element representing a getter + * @return A string property name + */ + private String inferPropertyNameFromGetter(ExecutableElement getter) { + final String name = getter.getSimpleName().toString(); + + if (GETTER_GET_PREFIX.matcher(name).find()) { + return name.substring(3, 4).toLowerCase() + name.substring(4); + } else if (isBoolean(getter.getReturnType()) && GETTER_IS_PREFIX.matcher(name).find()) { + return name.substring(2, 3).toLowerCase() + name.substring(3); + } else { + return name; + } + } + + /** + * Determine if a {@link TypeMirror} is a boxed or unboxed boolean. + * + * @param type The type mirror to check + * @return True if the type is a boolean + */ + private boolean isBoolean(TypeMirror type) { + if (type.getKind() == TypeKind.DECLARED) { + return mProcessingEnv.getTypeUtils().unboxedType(type).getKind() == TypeKind.BOOLEAN; + } else { + return type.getKind() == TypeKind.BOOLEAN; + } + } + + /** + * Unbox a type mirror if it represents a boxed type, otherwise pass it through. + * + * @param typeMirror The type mirror to unbox + * @return The same type mirror, or an unboxed primitive version + */ + private TypeKind unboxType(TypeMirror typeMirror) { + final TypeKind typeKind = typeMirror.getKind(); + + if (typeKind.isPrimitive()) { + return typeKind; + } else if (typeKind == TypeKind.DECLARED) { + try { + return mProcessingEnv.getTypeUtils().unboxedType(typeMirror).getKind(); + } catch (IllegalArgumentException e) { + return typeKind; + } + } else { + return typeKind; + } + } + + /** + * Determine if a type mirror represents a subtype of {@link android.graphics.Color}. + * + * @param typeMirror The type mirror to test + * @return True if it represents a subclass of color, false otherwise + */ + private boolean isColorType(TypeMirror typeMirror) { + final TypeElement colorType = mProcessingEnv + .getElementUtils() + .getTypeElement("android.graphics.Color"); + + if (colorType == null) { + return false; + } else { + return mProcessingEnv.getTypeUtils().isSubtype(typeMirror, colorType.asType()); + } + } +} diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java index 3b85dbb77d5c..dd4d8f54fb68 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,12 +51,6 @@ public final class InspectionCompanionGenerator { private static final ClassName R_CLASS_NAME = ClassName.get("android", "R"); /** - * The class name of {@link android.content.res.ResourceId}. - */ - private static final ClassName RESOURCE_ID_CLASS_NAME = ClassName.get( - "android.content.res", "ResourceId"); - - /** * The class name of {@link android.view.inspector.InspectionCompanion}. */ private static final ClassName INSPECTION_COMPANION = ClassName.get( @@ -91,11 +85,11 @@ public final class InspectionCompanionGenerator { private static final String GENERATED_CLASS_SUFFIX = "$$InspectionCompanion"; /** - * The null resource ID. + * The null resource ID, copied to avoid a host dependency on platform code. * - * @see android.content.res.ResourceId#ID_NULL + * @see android.content.res.Resources#ID_NULL */ - private static final int NO_ID = 0; + private static final int ID_NULL = 0; /** * @param filer A filer to write the generated source to @@ -289,8 +283,8 @@ public final class InspectionCompanionGenerator { if (property.isAttributeIdInferrableFromR()) { builder.add("$T.attr.$L", R_CLASS_NAME, property.getName()); } else { - if (property.getAttributeId() == NO_ID) { - builder.add("$T.ID_NULL", RESOURCE_ID_CLASS_NAME); + if (property.getAttributeId() == ID_NULL) { + builder.add("$L", ID_NULL); } else { builder.add("$L", String.format("0x%08x", property.getAttributeId())); } diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java index 3ffcff8a87d3..6f522608a9b4 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java index e531b67d9ea2..455f5b08e49e 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,11 +46,14 @@ import javax.lang.model.element.TypeElement; * @see android.view.inspector.InspectableProperty */ @SupportedAnnotationTypes({ - PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME + PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME, + PlatformInspectableProcessor.PROPERTY_QUALIFIED_NAME }) public final class PlatformInspectableProcessor extends AbstractProcessor { static final String NODE_NAME_QUALIFIED_NAME = "android.view.inspector.InspectableNodeName"; + static final String PROPERTY_QUALIFIED_NAME = + "android.view.inspector.InspectableProperty"; @Override public SourceVersion getSupportedSourceVersion() { @@ -68,6 +71,11 @@ public final class PlatformInspectableProcessor extends AbstractProcessor { new InspectableNodeNameProcessor(NODE_NAME_QUALIFIED_NAME, processingEnv), modelMap); + } else if (annotation.getQualifiedName().contentEquals(PROPERTY_QUALIFIED_NAME)) { + runModelProcessor( + roundEnv.getElementsAnnotatedWith(annotation), + new InspectablePropertyProcessor(PROPERTY_QUALIFIED_NAME, processingEnv), + modelMap); } else { fail("Unexpected annotation type", annotation); diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java index 6360e0a2de39..b4c6466b2b2f 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor index fa4f71ffd0fa..79185cc80f04 100644 --- a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor +++ b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor @@ -1 +1 @@ -android.processor.inspector.view.PlatformInspectableProcessor +android.processor.view.inspector.PlatformInspectableProcessor diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java index f639719800f6..b0775dc77f45 100644 --- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java +++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import android.processor.view.inspector.InspectableClassModel.Property; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; -import static junit.framework.TestCase.fail; +import static junit.framework.Assert.fail; import com.google.common.base.Charsets; import com.google.common.io.Resources; @@ -65,27 +65,28 @@ public class InspectionCompanionGeneratorTest { @Test public void testSimpleProperties() { - addProperty("boolean", Property.Type.BOOLEAN, "getBoolean"); - addProperty("byte", Property.Type.BYTE, "getByte"); - addProperty("char", Property.Type.CHAR, "getChar"); - addProperty("double", Property.Type.DOUBLE, "getDouble"); - addProperty("float", Property.Type.FLOAT, "getFloat"); - addProperty("int", Property.Type.INT, "getInt"); - addProperty("long", Property.Type.LONG, "getLong"); - addProperty("short", Property.Type.SHORT, "getShort"); - - addProperty("object", Property.Type.OBJECT, "getObject"); - addProperty("color", Property.Type.COLOR, "getColor"); - addProperty("gravity", Property.Type.GRAVITY, "getGravity"); + addProperty("boolean", "getBoolean", Property.Type.BOOLEAN); + addProperty("byte", "getByte", Property.Type.BYTE); + addProperty("char", "getChar", Property.Type.CHAR); + addProperty("double", "getDouble", Property.Type.DOUBLE); + addProperty("float", "getFloat", Property.Type.FLOAT); + addProperty("int", "getInt", Property.Type.INT); + addProperty("long", "getLong", Property.Type.LONG); + addProperty("short", "getShort", Property.Type.SHORT); + + addProperty("object", "getObject", Property.Type.OBJECT); + addProperty("color", "getColor", Property.Type.COLOR); + addProperty("gravity", "getGravity", Property.Type.GRAVITY); assertGeneratedFileEquals("SimpleProperties"); } @Test public void testNoAttributeId() { - final Property property = new Property("noAttributeProperty"); - property.setType(Property.Type.INT); - property.setGetter("getNoAttributeProperty"); + final Property property = new Property( + "noAttributeProperty", + "getNoAttributeProperty", + Property.Type.INT); property.setAttributeIdInferrableFromR(false); mModel.putProperty(property); @@ -94,19 +95,18 @@ public class InspectionCompanionGeneratorTest { @Test public void testSuppliedAttributeId() { - final Property property = new Property("suppliedAttributeProperty"); - property.setType(Property.Type.INT); - property.setGetter("getSuppliedAttributeProperty"); + final Property property = new Property( + "suppliedAttributeProperty", + "getSuppliedAttributeProperty", + Property.Type.INT); property.setAttributeId(0xdecafbad); mModel.putProperty(property); assertGeneratedFileEquals("SuppliedAttributeId"); } - private Property addProperty(String name, Property.Type type, String getter) { - final Property property = new Property(name); - property.setType(type); - property.setGetter(getter); + private Property addProperty(String name, String getter, Property.Type type) { + final Property property = new Property(name, getter, type); mModel.putProperty(property); return property; } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt index 277e84065fb7..23d0f7807aa5 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt @@ -1,6 +1,5 @@ package com.android.inspectable; -import android.content.res.ResourceId; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; import android.view.inspector.PropertyReader; @@ -25,7 +24,7 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom @Override public void mapProperties(PropertyMapper propertyMapper) { - mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", ResourceId.ID_NULL); + mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", 0); mPropertiesMapped = true; } diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java index 3b4a6cd7508c..b8c82fd9e0ae 100644 --- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -101,7 +101,7 @@ public abstract class EasyConnectStatusCallback { /** * Easy Connect Failure event: General protocol failure. */ - public static final int EASY_CONNECT_EVENT_FAILURE = -7; + public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; /** * Easy Connect Failure event: Feature or option is not supported. @@ -123,7 +123,7 @@ public abstract class EasyConnectStatusCallback { EASY_CONNECT_EVENT_FAILURE_CONFIGURATION, EASY_CONNECT_EVENT_FAILURE_BUSY, EASY_CONNECT_EVENT_FAILURE_TIMEOUT, - EASY_CONNECT_EVENT_FAILURE, + EASY_CONNECT_EVENT_FAILURE_GENERIC, EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED, EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, }) diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 07f7cb39a78c..46c419130233 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -46,7 +46,7 @@ import android.os.WorkSource; */ interface IWifiManager { - int getSupportedFeatures(); + long getSupportedFeatures(); WifiActivityEnergyInfo reportActivityInfo(); 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 559f4ad8264e..5742bd500795 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; /** @@ -1174,7 +1176,9 @@ public class WifiManager { * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * <b>Compatibility Note:</b> For applications targeting - * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will return an empty list, + * except to callers with Carrier privilege which will receive a restricted list only + * containing configurations which they created. */ @Deprecated @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE}) @@ -1219,7 +1223,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 +1266,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 +1293,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 +1743,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 +1767,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(); @@ -2055,7 +2083,7 @@ public class WifiManager { /** @hide */ public static final int WIFI_FEATURE_DPP = 0x80000000; // DPP (Easy-Connect) - private int getSupportedFeatures() { + private long getSupportedFeatures() { try { return mService.getSupportedFeatures(); } catch (RemoteException e) { @@ -2063,7 +2091,7 @@ public class WifiManager { } } - private boolean isFeatureSupported(int feature) { + private boolean isFeatureSupported(long feature) { return (getSupportedFeatures() & feature) == feature; } /** @@ -4323,6 +4351,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(); diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java index 955e040a46f1..aa1669ee6d94 100644 --- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java @@ -168,8 +168,9 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements @Override public String toString() { StringBuilder sb = new StringBuilder("WifiNetworkAgentSpecifier ["); - sb.append("WifiConfiguration=").append( - mWifiConfiguration == null ? null : mWifiConfiguration.configKey()) + sb.append("WifiConfiguration=") + .append(", SSID=").append(mWifiConfiguration.SSID) + .append(", BSSID=").append(mWifiConfiguration.BSSID) .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid) .append("]"); return sb.toString(); @@ -180,4 +181,9 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used " + "for requests."); } + + @Override + public NetworkSpecifier redact() { + return null; + } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 4348399b404b..6e4eeef4dd55 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -162,11 +162,11 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc @Override public String toString() { return new StringBuilder() - .append("WifiNetworkSpecifierWifiNetworkSpecifier [") + .append("WifiNetworkSpecifier [") .append(", SSID Match pattern=").append(ssidPatternMatcher) .append(", BSSID Match pattern=").append(bssidPatternMatcher) - .append(", WifiConfiguration=").append( - wifiConfiguration == null ? null : wifiConfiguration.configKey()) + .append(", SSID=").append(wifiConfiguration.SSID) + .append(", BSSID=").append(wifiConfiguration.BSSID) .append(", requestorUid=").append(requestorUid) .append("]") .toString(); diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 760f1e6bc5e2..3c90eb763e81 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -130,7 +130,8 @@ public final class WifiNetworkSuggestion implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [") - .append(", WifiConfiguration=").append(wifiConfiguration) + .append(", SSID=").append(wifiConfiguration.SSID) + .append(", BSSID=").append(wifiConfiguration.BSSID) .append(", isAppInteractionRequired=").append(isAppInteractionRequired) .append(", isUserInteractionRequired=").append(isUserInteractionRequired) .append(", suggestorUid=").append(suggestorUid) diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java index 14263830660f..4bee837d6089 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -149,6 +149,11 @@ public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements "WifiAwareAgentNetworkSpecifier should not be used in network requests"); } + @Override + public NetworkSpecifier redact() { + return null; + } + private void initialize() { try { mDigester = MessageDigest.getInstance("SHA-256"); 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/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java index 2c96c057e37d..b3ac9f15eb9a 100644 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ b/wifi/java/com/android/server/wifi/BaseWifiService.java @@ -64,7 +64,7 @@ public class BaseWifiService extends IWifiManager.Stub { private static final String TAG = BaseWifiService.class.getSimpleName(); @Override - public int getSupportedFeatures() { + public long getSupportedFeatures() { throw new UnsupportedOperationException(); } |