diff options
272 files changed, 9088 insertions, 2730 deletions
diff --git a/Android.bp b/Android.bp index 8c8563139083..a61b5472e6fc 100644 --- a/Android.bp +++ b/Android.bp @@ -226,6 +226,7 @@ filegroup { ":framework-wifi-non-updatable-sources", ":PacProcessor-aidl-sources", ":ProxyHandler-aidl-sources", + ":net-utils-framework-common-srcs", // AIDL from frameworks/base/native/ ":platform-compat-native-aidl", @@ -348,7 +349,6 @@ java_library { "android.hardware.vibrator-V1.1-java", "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", - "android.hardware.wifi-V1.0-java-constants", "devicepolicyprotosnano", "com.android.sysprop.apex", @@ -487,7 +487,7 @@ java_library { "framework-minus-apex", "updatable_media_stubs", "framework_mediaprovider_stubs", - "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs + "framework-appsearch-stubs", "framework-sdkextensions-stubs-systemapi", // TODO(b/146167933): Use framework-statsd-stubs instead. "framework-statsd", @@ -506,7 +506,10 @@ java_library { defaults: ["framework-defaults"], srcs: [":framework-all-sources"], installable: false, - static_libs: ["exoplayer2-core"], + static_libs: [ + "exoplayer2-core", + "android.hardware.wifi-V1.0-java-constants", + ], apex_available: ["//apex_available:platform"], } @@ -604,17 +607,22 @@ gensrcs { filegroup { name: "framework-annotations", srcs: [ + "core/java/android/annotation/CallbackExecutor.java", + "core/java/android/annotation/CheckResult.java", "core/java/android/annotation/IntDef.java", "core/java/android/annotation/IntRange.java", "core/java/android/annotation/NonNull.java", "core/java/android/annotation/Nullable.java", "core/java/android/annotation/RequiresPermission.java", "core/java/android/annotation/SdkConstant.java", + "core/java/android/annotation/StringDef.java", "core/java/android/annotation/SystemApi.java", + "core/java/android/annotation/SystemService.java", "core/java/android/annotation/TestApi.java", "core/java/android/annotation/UnsupportedAppUsage.java", "core/java/com/android/internal/annotations/GuardedBy.java", "core/java/com/android/internal/annotations/VisibleForTesting.java", + "core/java/com/android/internal/annotations/Immutable.java", ], } @@ -629,6 +637,9 @@ filegroup { visibility: ["//frameworks/opt/net/ike"], srcs: [ "core/java/android/net/annotations/PolicyDirection.java", + "core/java/com/android/internal/util/IState.java", + "core/java/com/android/internal/util/State.java", + "core/java/com/android/internal/util/StateMachine.java", "telephony/java/android/telephony/Annotation.java", ], } @@ -1148,13 +1159,36 @@ filegroup { ], } +// utility classes statically linked into framework-wifi and dynamically linked +// into wifi-service +java_library { + name: "framework-wifi-util-lib", + sdk_version: "core_current", + srcs: [ + "core/java/android/content/pm/BaseParceledListSlice.java", + "core/java/android/content/pm/ParceledListSlice.java", + "core/java/android/net/shared/Inet4AddressUtils.java", + "core/java/android/os/HandlerExecutor.java", + "core/java/com/android/internal/util/AsyncChannel.java", + "core/java/com/android/internal/util/AsyncService.java", + "core/java/com/android/internal/util/Protocol.java", + "core/java/com/android/internal/util/Preconditions.java", + "telephony/java/android/telephony/Annotation.java", + ], + libs: [ + "framework-annotations-lib", + "unsupportedappusage", + "android_system_stubs_current", + ], + visibility: ["//frameworks/base/wifi"], +} + +// utility classes statically linked into wifi-service filegroup { name: "framework-wifi-service-shared-srcs", srcs: [ - ":framework-annotations", "core/java/android/net/InterfaceConfiguration.java", "core/java/android/os/BasicShellCommandHandler.java", - "core/java/android/os/HandlerExecutor.java", "core/java/android/util/BackupUtils.java", "core/java/android/util/LocalLog.java", "core/java/android/util/Rational.java", @@ -1162,11 +1196,9 @@ filegroup { "core/java/com/android/internal/util/HexDump.java", "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/MessageUtils.java", - "core/java/com/android/internal/util/Preconditions.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", "core/java/com/android/internal/util/WakeupMessage.java", - "core/java/com/android/internal/util/XmlUtils.java", ], } diff --git a/StubLibraries.bp b/StubLibraries.bp index d1950474da5a..baa3c615039d 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -41,10 +41,9 @@ packages_to_document = [ ] stubs_defaults { - name: "metalava-api-stubs-default", + name: "metalava-non-updatable-api-stubs-default", srcs: [ ":framework-non-updatable-sources", - ":framework-updatable-sources", "core/java/**/*.logtags", ":opt-telephony-srcs", ":opt-net-voip-srcs", @@ -64,14 +63,23 @@ stubs_defaults { "sdk-dir", "api-versions-jars-dir", ], - sdk_version: "core_platform", filter_packages: packages_to_document, } +stubs_defaults { + name: "metalava-api-stubs-default", + defaults: ["metalava-non-updatable-api-stubs-default"], + srcs: [":framework-updatable-sources"], + sdk_version: "core_platform", +} + ///////////////////////////////////////////////////////////////////// // *-api-stubs-docs modules providing source files for the stub libraries ///////////////////////////////////////////////////////////////////// +// api-stubs-docs, system-api-stubs-docs, and test-api-stubs-docs have APIs +// from the non-updatable part of the platform as well as from the updatable +// modules droidstubs { name: "api-stubs-docs", defaults: ["metalava-api-stubs-default"], @@ -112,7 +120,10 @@ droidstubs { arg_files: [ "core/res/AndroidManifest.xml", ], - args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)", + args: metalava_framework_docs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\)", check_api: { current: { api_file: "api/system-current.txt", @@ -155,6 +166,111 @@ droidstubs { } ///////////////////////////////////////////////////////////////////// +// Following droidstubs modules are for extra APIs for modules. +// The framework currently have two more API surfaces for modules: +// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES) +///////////////////////////////////////////////////////////////////// + +// TODO(b/146727827) remove the *-api modules when we can teach metalava +// about the relationship among the API surfaces. Currently, these modules are only to generate +// the API signature files and ensure that the APIs evolve in a backwards compatible manner. +// They however are NOT used for building the API stub. +droidstubs { + name: "module-app-api", + defaults: ["metalava-non-updatable-api-stubs-default"], + libs: ["framework-all"], + arg_files: ["core/res/AndroidManifest.xml"], + args: metalava_framework_docs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\)", + check_api: { + current: { + api_file: "api/module-app-current.txt", + removed_api_file: "api/module-app-removed.txt", + }, + // TODO(b/147559833) enable the compatibility check against the last release API + // and the API lint + //last_released: { + // api_file: ":last-released-module-app-api", + // removed_api_file: "api/module-app-removed.txt", + // baseline_file: ":module-app-api-incompatibilities-with-last-released" + //}, + //api_lint: { + // enabled: true, + // new_since: ":last-released-module-app-api", + // baseline_file: "api/module-app-lint-baseline.txt", + //}, + }, + //jdiff_enabled: true, +} + +droidstubs { + name: "module-lib-api", + defaults: ["metalava-non-updatable-api-stubs-default"], + libs: ["framework-all"], + arg_files: ["core/res/AndroidManifest.xml"], + args: metalava_framework_docs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," + + "process=android.annotation.SystemApi.Process.ALL\\)", + check_api: { + current: { + api_file: "api/module-lib-current.txt", + removed_api_file: "api/module-lib-removed.txt", + }, + // TODO(b/147559833) enable the compatibility check against the last release API + // and the API lint + //last_released: { + // api_file: ":last-released-module-lib-api", + // removed_api_file: "api/module-lib-removed.txt", + // baseline_file: ":module-lib-api-incompatibilities-with-last-released" + //}, + //api_lint: { + // enabled: true, + // new_since: ":last-released-module-lib-api", + // baseline_file: "api/module-lib-lint-baseline.txt", + //}, + }, + //jdiff_enabled: true, +} + +// The following two droidstubs modules generate source files for the API stub libraries for +// modules. Note that they not only include their own APIs but also other APIs that have +// narrower scope. For example, module-lib-api-stubs-docs includes all @SystemApis not just +// the ones with 'client=MODULE_LIBRARIES'. +droidstubs { + name: "module-app-api-stubs-docs", + defaults: ["metalava-non-updatable-api-stubs-default"], + libs: ["framework-all"], + arg_files: ["core/res/AndroidManifest.xml"], + args: metalava_framework_docs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\)" + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\)", +} + +droidstubs { + name: "module-lib-api-stubs-docs", + defaults: ["metalava-non-updatable-api-stubs-default"], + libs: ["framework-all"], + arg_files: ["core/res/AndroidManifest.xml"], + args: metalava_framework_docs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\)" + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\)" + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," + + "process=android.annotation.SystemApi.Process.ALL\\)", +} + +///////////////////////////////////////////////////////////////////// // android_*_stubs_current modules are the stubs libraries compiled // from *-api-stubs-docs ///////////////////////////////////////////////////////////////////// @@ -169,7 +285,6 @@ java_defaults { java_resources: [ ":notices-for-framework-stubs", ], - sdk_version: "core_current", system_modules: "none", java_version: "1.8", compile_dex: true, @@ -187,6 +302,7 @@ java_library_static { "private-stub-annotations-jar", ], defaults: ["framework-stubs-default"], + sdk_version: "core_current", } java_library_static { @@ -201,6 +317,7 @@ java_library_static { "private-stub-annotations-jar", ], defaults: ["framework-stubs-default"], + sdk_version: "core_current", } java_library_static { @@ -215,6 +332,37 @@ java_library_static { "private-stub-annotations-jar", ], defaults: ["framework-stubs-default"], + sdk_version: "core_current", +} + +java_library_static { + name: "framework_module_app_stubs_current", + srcs: [ + ":module-app-api-stubs-docs", + ], + libs: [ + "stub-annotations", + "framework-all", + ], + static_libs: [ + "private-stub-annotations-jar", + ], + defaults: ["framework-stubs-default"], +} + +java_library_static { + name: "framework_module_lib_stubs_current", + srcs: [ + ":module-lib-api-stubs-docs", + ], + libs: [ + "stub-annotations", + "framework-all", + ], + static_libs: [ + "private-stub-annotations-jar", + ], + defaults: ["framework-stubs-default"], } ///////////////////////////////////////////////////////////////////// diff --git a/apex/Android.bp b/apex/Android.bp index 56f7db2c8dad..abebfa39fada 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -37,6 +37,36 @@ stubs_defaults { stubs_defaults { name: "framework-module-stubs-defaults-systemapi", - args: mainline_stubs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ", + args: mainline_stubs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\) ", + installable: false, +} + +stubs_defaults { + name: "framework-module-stubs-defaults-module_apps_api", + args: mainline_stubs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\) " + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\) ", + installable: false, +} + +stubs_defaults { + name: "framework-module-stubs-defaults-module_libs_api", + args: mainline_stubs_args + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\) " + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_APPS," + + "process=android.annotation.SystemApi.Process.ALL\\) " + + " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," + + "process=android.annotation.SystemApi.Process.ALL\\) ", installable: false, } diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index 1f30dda21ef7..60cc3bec0a9d 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -30,29 +30,10 @@ java_library { libs: [ "framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs ], - visibility: [ - "//frameworks/base/apex/appsearch:__subpackages__", - // TODO(b/146218515) remove this when framework is built with the stub of appsearch - "//frameworks/base", - ], + visibility: ["//frameworks/base/apex/appsearch:__subpackages__"], apex_available: ["com.android.appsearch"], } -metalava_appsearch_docs_args = - "--hide-package com.android.server " + - "--error UnhiddenSystemApi " + - "--hide RequiresPermission " + - "--hide MissingPermission " + - "--hide BroadcastBehavior " + - "--hide HiddenSuperclass " + - "--hide DeprecationMismatch " + - "--hide UnavailableSymbol " + - "--hide SdkConstant " + - "--hide HiddenTypeParameter " + - "--hide Todo --hide Typo " + - "--hide HiddenTypedefConstant " + - "--show-annotation android.annotation.SystemApi " - droidstubs { name: "framework-appsearch-stubs-srcs", srcs: [ @@ -62,9 +43,8 @@ droidstubs { aidl: { include_dirs: ["frameworks/base/core/java"], }, - args: metalava_appsearch_docs_args, - sdk_version: "core_current", - libs: ["android_system_stubs_current"], + defaults: ["framework-module-stubs-defaults-systemapi"], + sdk_version: "system_current", } java_library { @@ -75,7 +55,6 @@ java_library { "java", ], }, - sdk_version: "core_current", - libs: ["android_system_stubs_current"], + sdk_version: "system_current", installable: false, } diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp index 4ebafce84886..e7abcd9a645a 100644 --- a/apex/appsearch/service/Android.bp +++ b/apex/appsearch/service/Android.bp @@ -20,8 +20,10 @@ java_library { libs: [ "framework", "services.core", + "framework-appsearch", ], static_libs: [ "icing-java-proto-lite", - ] + ], + apex_available: [ "com.android.appsearch" ], } diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 3f58c72cbdc2..f8b2f32e1a2f 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -761,6 +761,7 @@ public class DeviceIdleController extends SystemService @Override public void onTrigger(TriggerEvent event) { synchronized (DeviceIdleController.this) { + // One_shot sensors (which call onTrigger) are unregistered when onTrigger is called active = false; motionLocked(); } @@ -769,6 +770,9 @@ public class DeviceIdleController extends SystemService @Override public void onSensorChanged(SensorEvent event) { synchronized (DeviceIdleController.this) { + // Since one_shot sensors are unregistered when onTrigger is called, unregister + // listeners here so that the MotionListener is in a consistent state when it calls + // out to motionLocked. mSensorManager.unregisterListener(this, mMotionSensor); active = false; motionLocked(); diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp index f3a8989e633a..910384845810 100644 --- a/apex/statsd/service/Android.bp +++ b/apex/statsd/service/Android.bp @@ -13,4 +13,8 @@ java_library { "framework-minus-apex", "services.core", ], + apex_available: [ + "com.android.os.statsd", + "test_com.android.os.statsd", + ], } diff --git a/api/current.txt b/api/current.txt index d1e1108b23b7..16a9e49da032 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2925,6 +2925,7 @@ package android.accessibilityservice { method public int getShowMode(); method public boolean removeOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); method public boolean setShowMode(int); + method public boolean switchToInputMethod(@NonNull String); } public static interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener { @@ -6763,6 +6764,7 @@ package android.app.admin { method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String); method public CharSequence getDeviceOwnerLockScreenInfo(); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); + method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName); method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName); method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); method @NonNull public java.util.List<byte[]> getInstalledCaCerts(@Nullable android.content.ComponentName); @@ -6881,6 +6883,7 @@ package android.app.admin { method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>); method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence); method public void setEndUserSessionMessage(@NonNull android.content.ComponentName, @Nullable CharSequence); + method public void setFactoryResetProtectionPolicy(@NonNull android.content.ComponentName, @Nullable android.app.admin.FactoryResetProtectionPolicy); method public int setGlobalPrivateDnsModeOpportunistic(@NonNull android.content.ComponentName); method @WorkerThread public int setGlobalPrivateDnsModeSpecifiedHost(@NonNull android.content.ComponentName, @NonNull String); method public void setGlobalSetting(@NonNull android.content.ComponentName, String, String); @@ -7115,6 +7118,21 @@ package android.app.admin { field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DnsEvent> CREATOR; } + public final class FactoryResetProtectionPolicy implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getFactoryResetProtectionAccounts(); + method public boolean isFactoryResetProtectionDisabled(); + method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FactoryResetProtectionPolicy> CREATOR; + } + + public static class FactoryResetProtectionPolicy.Builder { + ctor public FactoryResetProtectionPolicy.Builder(); + method @NonNull public android.app.admin.FactoryResetProtectionPolicy build(); + method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionAccounts(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionDisabled(boolean); + } + public class FreezePeriod { ctor public FreezePeriod(java.time.MonthDay, java.time.MonthDay); method public java.time.MonthDay getEnd(); @@ -10008,6 +10026,7 @@ package android.content { field public static final String MEDIA_ROUTER_SERVICE = "media_router"; field public static final String MEDIA_SESSION_SERVICE = "media_session"; field public static final String MIDI_SERVICE = "midi"; + field public static final String MMS_SERVICE = "mms"; field public static final int MODE_APPEND = 32768; // 0x8000 field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8 field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4 @@ -17061,13 +17080,13 @@ package android.hardware.camera2 { method public abstract void close(); method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException; method @NonNull public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException; - method public abstract void createCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException; - method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; - method public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(@NonNull android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException; - method public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; - method public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException; method @NonNull public abstract String getId(); method public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException; @@ -28682,7 +28701,9 @@ package android.media.tv { ctor public TvInputService(); method public final android.os.IBinder onBind(android.content.Intent); method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String); + method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String); method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String); + method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String); field public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService"; field public static final String SERVICE_META_DATA = "android.media.tv.input"; } @@ -30625,6 +30646,7 @@ package android.net.wifi { field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION"; + field public static final String ACTION_WIFI_SCAN_AVAILABLE = "android.net.wifi.action.WIFI_SCAN_AVAILABLE"; field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1 field @Deprecated public static final String EXTRA_BSSID = "bssid"; field public static final String EXTRA_NETWORK_INFO = "networkInfo"; @@ -30633,6 +30655,7 @@ package android.net.wifi { field @Deprecated public static final String EXTRA_NEW_STATE = "newState"; field public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; field public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated"; + field public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE"; field @Deprecated public static final String EXTRA_SUPPLICANT_CONNECTED = "connected"; field @Deprecated public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError"; field @Deprecated public static final String EXTRA_WIFI_INFO = "wifiInfo"; @@ -45141,7 +45164,7 @@ package android.telephony { field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; - field public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; + field @Deprecated public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool"; field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool"; @@ -45164,6 +45187,7 @@ package android.telephony { field public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int"; field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool"; field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string"; + field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool"; field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool"; @@ -45632,6 +45656,11 @@ package android.telephony { method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback); } + public class MmsManager { + method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent); + method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent); + } + @Deprecated public class NeighboringCellInfo implements android.os.Parcelable { ctor @Deprecated public NeighboringCellInfo(); ctor @Deprecated public NeighboringCellInfo(int, int); @@ -45869,8 +45898,8 @@ package android.telephony { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); - method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); - method @Nullable public android.os.Bundle getCarrierConfigValues(); + method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method @NonNull public android.os.Bundle getCarrierConfigValues(); method public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int); @@ -45879,7 +45908,7 @@ package android.telephony { method public int getSubscriptionId(); method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); - method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent); diff --git a/api/module-app-current.txt b/api/module-app-current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/api/module-app-current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/api/module-app-removed.txt b/api/module-app-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/api/module-app-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/api/module-lib-current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/api/module-lib-removed.txt b/api/module-lib-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/api/system-current.txt b/api/system-current.txt index c2aef749587e..46ffc7f7c45c 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -190,6 +190,7 @@ package android { field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES"; field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS"; field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS"; + field public static final String SECURE_ELEMENT_PRIVILEGED = "android.permission.SECURE_ELEMENT_PRIVILEGED"; field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY"; field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"; field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION"; @@ -210,6 +211,7 @@ package android { field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA"; field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED"; + field public static final String TUNER_RESOURCE_ACCESS = "android.permission.TUNER_RESOURCE_ACCESS"; field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE"; field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER"; field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS"; @@ -282,6 +284,7 @@ package android { field public static final int config_helpIntentNameKey = 17039390; // 0x104001e field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c + field public static final int config_systemGallery = 17039402; // 0x104002a } public static final class R.style { @@ -814,6 +817,7 @@ package android.app.admin { field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION"; field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE"; field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE"; + field public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED"; field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER"; field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE"; field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME"; @@ -1330,6 +1334,10 @@ package android.app.usage { field public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService"; } + public class NetworkStatsManager { + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider); + } + public static final class UsageEvents.Event { method public int getInstanceId(); method @Nullable public String getNotificationChannelId(); @@ -1706,6 +1714,7 @@ package android.content { field public static final String NETD_SERVICE = "netd"; field public static final String NETWORK_POLICY_SERVICE = "netpolicy"; field public static final String NETWORK_SCORE_SERVICE = "network_score"; + field public static final String NETWORK_STACK_SERVICE = "network_stack"; field public static final String OEM_LOCK_SERVICE = "oem_lock"; field public static final String PERMISSION_SERVICE = "permission"; field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; @@ -2408,7 +2417,7 @@ package android.hardware.biometrics { package android.hardware.camera2 { public abstract class CameraDevice implements java.lang.AutoCloseable { - method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1 field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0 field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000 @@ -4397,10 +4406,12 @@ package android.media.soundtrigger { } public static class SoundTriggerManager.Model { - method public static android.media.soundtrigger.SoundTriggerManager.Model create(java.util.UUID, java.util.UUID, byte[]); - method public byte[] getModelData(); - method public java.util.UUID getModelUuid(); - method public java.util.UUID getVendorUuid(); + method @NonNull public static android.media.soundtrigger.SoundTriggerManager.Model create(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], int); + method @NonNull public static android.media.soundtrigger.SoundTriggerManager.Model create(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[]); + method @Nullable public byte[] getModelData(); + method @NonNull public java.util.UUID getModelUuid(); + method @NonNull public java.util.UUID getVendorUuid(); + method public int getVersion(); } } @@ -4530,6 +4541,7 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); + method @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) public int getClientPid(@NonNull String); method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList(); method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList(); method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList(); @@ -4541,6 +4553,7 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware); method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean); + field public static final int UNKNOWN_CLIENT_PID = -1; // 0xffffffff } public static final class TvInputManager.Hardware { @@ -4617,6 +4630,28 @@ package android.media.tv.tuner { method public abstract int getType(); } + public class Lnb implements java.lang.AutoCloseable { + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close(); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int sendDiseqcMessage(@NonNull byte[]); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setSatellitePosition(int); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setTone(int); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setVoltage(int); + field public static final int POSITION_A = 1; // 0x1 + field public static final int POSITION_B = 2; // 0x2 + field public static final int POSITION_UNDEFINED = 0; // 0x0 + field public static final int TONE_CONTINUOUS = 1; // 0x1 + field public static final int TONE_NONE = 0; // 0x0 + field public static final int VOLTAGE_11V = 2; // 0x2 + field public static final int VOLTAGE_12V = 3; // 0x3 + field public static final int VOLTAGE_13V = 4; // 0x4 + field public static final int VOLTAGE_14V = 5; // 0x5 + field public static final int VOLTAGE_15V = 6; // 0x6 + field public static final int VOLTAGE_18V = 7; // 0x7 + field public static final int VOLTAGE_19V = 8; // 0x8 + field public static final int VOLTAGE_5V = 1; // 0x1 + field public static final int VOLTAGE_NONE = 0; // 0x0 + } + public final class Tuner implements java.lang.AutoCloseable { ctor public Tuner(@NonNull android.content.Context); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler(); @@ -4639,6 +4674,13 @@ package android.media.tv.tuner { field public static final int FILTER_STATUS_HIGH_WATER = 4; // 0x4 field public static final int FILTER_STATUS_LOW_WATER = 2; // 0x2 field public static final int FILTER_STATUS_OVERFLOW = 8; // 0x8 + field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4 + field public static final int RESULT_INVALID_STATE = 3; // 0x3 + field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2 + field public static final int RESULT_OUT_OF_MEMORY = 5; // 0x5 + field public static final int RESULT_SUCCESS = 0; // 0x0 + field public static final int RESULT_UNAVAILABLE = 1; // 0x1 + field public static final int RESULT_UNKNOWN_ERROR = 6; // 0x6 } } @@ -4742,7 +4784,9 @@ package android.net { public class CaptivePortal implements android.os.Parcelable { method public void logEvent(int, @NonNull String); + method public void reevaluateNetwork(); method public void useNetwork(); + field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 field public static final int APP_RETURN_DISMISSED = 0; // 0x0 field public static final int APP_RETURN_UNWANTED = 1; // 0x1 field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 @@ -8111,7 +8155,6 @@ package android.provider { field @NonNull public static final String ENABLE_CMAS_PRESIDENTIAL_PREF = "enable_cmas_presidential_alerts"; field @NonNull public static final String ENABLE_CMAS_SEVERE_THREAT_PREF = "enable_cmas_severe_threat_alerts"; field @NonNull public static final String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts"; - field @NonNull public static final String ENABLE_FULL_VOLUME_PREF = "use_full_volume"; field @NonNull public static final String ENABLE_PUBLIC_SAFETY_PREF = "enable_public_safety_messages"; field @NonNull public static final String ENABLE_STATE_LOCAL_TEST_PREF = "enable_state_local_test_alerts"; field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts"; @@ -9347,6 +9390,7 @@ package android.telephony { public final class CallQuality implements android.os.Parcelable { ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int); + ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean); method public int describeContents(); method public int getAverageRelativeJitter(); method public int getAverageRoundTripTime(); @@ -9359,6 +9403,9 @@ package android.telephony { method public int getNumRtpPacketsTransmitted(); method public int getNumRtpPacketsTransmittedLost(); method public int getUplinkCallQualityLevel(); + method public boolean isIncomingSilenceDetected(); + method public boolean isOutgoingSilenceDetected(); + method public boolean isRtpInactivityDetected(); method public void writeToParcel(android.os.Parcel, int); field public static final int CALL_QUALITY_BAD = 4; // 0x4 field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0 @@ -10363,6 +10410,10 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>); } + public class SmsMessage { + method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int); + } + public class SubscriptionInfo implements android.os.Parcelable { method public boolean areUiccApplicationsEnabled(); method @Nullable public java.util.List<android.telephony.UiccAccessRule> getAccessRules(); @@ -10555,12 +10606,16 @@ package android.telephony { method public void updateServiceLocation(); method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String); field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED"; + field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; + field public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; + field public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + field public static final String ACTION_CARRIER_SIGNAL_RESET = "com.android.internal.telephony.CARRIER_SIGNAL_RESET"; field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED"; field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED"; field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE"; field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED"; field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED"; - field public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME"; field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE"; field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS"; field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; @@ -10572,6 +10627,15 @@ package android.telephony { field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION"; field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID"; + field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto"; + field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt"; + field @Deprecated public static final String EXTRA_APN_TYPE = "apnType"; + field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt"; + field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable"; + field public static final String EXTRA_ERROR_CODE = "errorCode"; + field public static final String EXTRA_PCO_ID = "pcoId"; + field public static final String EXTRA_PCO_VALUE = "pcoValue"; + field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl"; field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; @@ -11471,10 +11535,12 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19 field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13 diff --git a/api/test-current.txt b/api/test-current.txt index a8f7b51f8046..8c73255bd387 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -34,6 +34,7 @@ package android { public static final class R.string { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 + field public static final int config_systemGallery = 17039402; // 0x104002a } } @@ -756,6 +757,7 @@ package android.content { field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; + field public static final String NETWORK_STACK_SERVICE = "network_stack"; field public static final String PERMISSION_SERVICE = "permission"; field public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; field public static final String ROLLBACK_SERVICE = "rollback"; @@ -1020,7 +1022,7 @@ package android.graphics.drawable { package android.hardware.camera2 { public abstract class CameraDevice implements java.lang.AutoCloseable { - method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1 field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0 field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000 @@ -1496,7 +1498,9 @@ package android.net { public class CaptivePortal implements android.os.Parcelable { method public void logEvent(int, @NonNull String); + method public void reevaluateNetwork(); method public void useNetwork(); + field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 field public static final int APP_RETURN_DISMISSED = 0; // 0x0 field public static final int APP_RETURN_UNWANTED = 1; // 0x1 field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 @@ -2733,6 +2737,11 @@ package android.service.autofill { method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions(); } + public static final class Dataset.Builder { + ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); + } + public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } @@ -3127,6 +3136,7 @@ package android.telephony { public final class CallQuality implements android.os.Parcelable { ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int); + ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean); method public int describeContents(); method public int getAverageRelativeJitter(); method public int getAverageRoundTripTime(); @@ -3139,6 +3149,9 @@ package android.telephony { method public int getNumRtpPacketsTransmitted(); method public int getNumRtpPacketsTransmittedLost(); method public int getUplinkCallQualityLevel(); + method public boolean isIncomingSilenceDetected(); + method public boolean isOutgoingSilenceDetected(); + method public boolean isRtpInactivityDetected(); method public void writeToParcel(android.os.Parcel, int); field public static final int CALL_QUALITY_BAD = 4; // 0x4 field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0 @@ -3813,10 +3826,12 @@ package android.telephony.ims { method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public int getProvisioningIntValue(int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19 field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13 diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 459520a3eb27..8fac31a05c49 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1113,7 +1113,7 @@ void BootAnimation::handleViewport(nsecs_t timestep) { SurfaceComposerClient::Transaction t; t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset) .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight)); - t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect); + t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect); t.apply(); mTargetInset = mCurrentInset = 0; diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index 229628c7dd8b..407478945151 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -33,6 +33,8 @@ namespace android::idmap2 { namespace { +#define REWRITE_PACKAGE(resid, package_id) \ + (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U)) #define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24) std::string ConcatPolicies(const std::vector<std::string>& policies) { @@ -154,6 +156,7 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage return Error("root element is not <overlay> tag"); } + const uint8_t target_package_id = target_package->GetPackageId(); const uint8_t overlay_package_id = overlay_package->GetPackageId(); auto overlay_it_end = root_it.end(); for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) { @@ -187,6 +190,9 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage continue; } + // Retrieve the compile-time resource id of the target resource. + target_id = REWRITE_PACKAGE(target_id, target_package_id); + if (overlay_resource->dataType == Res_value::TYPE_STRING) { overlay_resource->data += string_pool_offset; } @@ -214,6 +220,7 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy( const AssetManager2* target_am, const AssetManager2* overlay_am, const LoadedPackage* target_package, const LoadedPackage* overlay_package) { ResourceMapping resource_mapping; + const uint8_t target_package_id = target_package->GetPackageId(); const auto end = overlay_package->end(); for (auto iter = overlay_package->begin(); iter != end; ++iter) { const ResourceId overlay_resid = *iter; @@ -225,11 +232,14 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy( // Find the resource with the same type and entry name within the target package. const std::string full_name = base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str()); - const ResourceId target_resource = target_am->GetResourceId(full_name); + ResourceId target_resource = target_am->GetResourceId(full_name); if (target_resource == 0U) { continue; } + // Retrieve the compile-time resource id of the target resource. + target_resource = REWRITE_PACKAGE(target_resource, target_package_id); + resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid, /* rewrite_overlay_reference */ false); } diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS index 04464ce02b4f..a61babf32e58 100644 --- a/cmds/statsd/OWNERS +++ b/cmds/statsd/OWNERS @@ -1,7 +1,8 @@ -jianjin@google.com +jeffreyhuang@google.com joeo@google.com jtnguyen@google.com muhammadq@google.com +ruchirr@google.com singhtejinder@google.com tsaichristine@google.com yaochen@google.com diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index 6fc1e236c661..967fd323e5a0 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -261,6 +261,11 @@ inline Matcher getSimpleMatcher(int32_t tag, size_t field) { return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000); } +inline Matcher getFirstUidMatcher(int32_t atomId) { + int32_t pos[] = {1, 1, 1}; + return Matcher(Field(atomId, pos, 2), 0xff7f7f7f); +} + /** * A wrapper for a union type to contain multiple types of values. * diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index 6d2bd04756ac..946c55087005 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -30,6 +30,8 @@ enum StateField { PRIMARY = 1; // The field that represents the state. It's an exclusive state. EXCLUSIVE = 2; + + PRIMARY_FIELD_FIRST_UID = 3; } // Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2bacfbc57395..bb01911ee836 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -126,10 +126,10 @@ message Atom { AppStartOccurred app_start_occurred = 48; AppStartCanceled app_start_canceled = 49; AppStartFullyDrawn app_start_fully_drawn = 50; - LmkKillOccurred lmk_kill_occurred = 51; + LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"]; PictureInPictureStateChanged picture_in_picture_state_changed = 52; WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"]; - LmkStateChanged lmk_state_changed = 54; + LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"]; AppStartMemoryStateCaptured app_start_memory_state_captured = 55; ShutdownSequenceReported shutdown_sequence_reported = 56; BootSequenceReported boot_sequence_reported = 57; @@ -334,6 +334,10 @@ message Atom { MediaProviderIdleMaintenance media_provider_idle_maintenance = 237 [(module) = "mediaprovider"]; RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238; + BootTimeEventDuration boot_time_event_duration_reported = 239; + BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240; + BootTimeEventUtcTime boot_time_event_utc_time_reported = 241; + BootTimeEventErrorCode boot_time_event_error_code_reported = 242; } // Pulled events will start at field 10000. @@ -908,14 +912,16 @@ message CameraStateChanged { * TODO */ message WakelockStateChanged { - repeated AttributionNode attribution_node = 1; + repeated AttributionNode attribution_node = 1 + [(state_field_option).option = PRIMARY_FIELD_FIRST_UID]; // The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock. // From frameworks/base/core/proto/android/os/enums.proto. - optional android.os.WakeLockLevelEnum type = 2; + optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).option = PRIMARY]; + ; // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). - optional string tag = 3; + optional string tag = 3 [(state_field_option).option = PRIMARY]; enum State { RELEASE = 0; @@ -923,7 +929,7 @@ message WakelockStateChanged { CHANGE_RELEASE = 2; CHANGE_ACQUIRE = 3; } - optional State state = 4; + optional State state = 4 [(state_field_option).option = EXCLUSIVE]; } /** @@ -3925,6 +3931,207 @@ message MediaProviderIdleMaintenance { optional float normalized_expired_media = 5; } +/** + * Represents boot time event with duration in ms. + * + * Logged from: bootstat and various system server components. Check each enums for details. + */ +message BootTimeEventDuration { + enum DurationEvent { + UNKNOWN = 0; + // Bootloader time excluding BOOTLOADER_UI_WAIT + boot complete time. Logged from bootstat. + ABSOLUTE_BOOT_TIME = 1; + // Bootloader's 1st stage execution time. + // Logged from bootstat. + BOOTLOADER_FIRST_STAGE_EXEC = 2; + // Bootloader's 1st stage loading time. + // Logged from bootstat. + BOOTLOADER_FIRST_STAGE_LOAD = 3; + // Bootloader's kernel loading time. + // Logged from bootstat. + BOOTLOADER_KERNEL_LOAD = 4; + // Bootloader's 2nd stage execution time. + // Logged from bootstat. + BOOTLOADER_SECOND_STAGE_EXEC = 5; + // Bootloader's 2nd stage loading time. + // Logged from bootstat. + BOOTLOADER_SECOND_STAGE_LOAD = 6; + // Duration for Bootloader to show unlocked device's warning UI. This should not happen + // for locked device. + // Logged from bootstat. + BOOTLOADER_UI_WAIT = 7; + // Total time spend in bootloader. This is the sum of all BOOTLOADER_* listed above. + // Logged from bootstat. + BOOTLOADER_TOTAL = 8; + // Shutdown duration inside init for the reboot before the current boot up. + // Logged from f/b/services/.../BootReceiver.java. + SHUTDOWN_DURATION = 9; + // Total time for mounting of disk devices during bootup. + // Logged from f/b/services/.../BootReceiver.java. + MOUNT_DEFAULT_DURATION = 10; + // Total time for early stage mounting of disk devices during bootup. + // Logged from f/b/services/.../BootReceiver.java. + MOUNT_EARLY_DURATION = 11; + // Total time for late stage mounting of disk devices during bootup. + // Logged from f/b/services/.../BootReceiver.java. + MOUNT_LATE_DURATION = 12; + // Average time to scan non-system app after OTA + // Logged from f/b/services/.../PackageManagerService.java + OTA_PACKAGE_MANAGER_INIT_TIME = 13; + // Time to initialize Package manager after OTA + // Logged from f/b/services/.../PackageManagerService.java + OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME = 14; + // Time to scan all system app from Package manager after OTA + // Logged from f/b/services/.../PackageManagerService.java + OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME = 15; + // Init's total time for cold boot stage. + // Logged from bootstat. + COLDBOOT_WAIT = 16; + // Init's total time for initializing selinux. + // Logged from bootstat. + SELINUX_INIT = 17; + // Time since last factory reset. + // Logged from bootstat. + FACTORY_RESET_TIME_SINCE_RESET = 18; + } + + // Type of the event. + optional DurationEvent event = 1; + // Duration of the event in ms. + optional int64 duration_millis = 2; +} + +/** + * Represents the start of specific boot time event during bootup in ms. This is usually a time + * since boot-up. + * + * Logged from: bootstat and various system server components. Check each enums for details. + */ +message BootTimeEventElapsedTime { + enum ElapsedTimeEvent { + UNKNOWN = 0; + // Time when init starts 1st stage. Logged from bootstat. + ANDROID_INIT_STAGE_1 = 1; + // Time when sys.boot_completed prop is set. + // Logged from bootstat. + BOOT_COMPLETE = 2; + // BOOT_COMPLETE for encrypted device. + BOOT_COMPLETE_ENCRYPTION = 3; + // BOOT_COMPLETE for device with no encryption. + BOOT_COMPLETE_NO_ENCRYPTION = 4; + // Adjusted BOOT_COMPLETE for encrypted device extracting decryption time. + BOOT_COMPLETE_POST_DESCRYPT = 5; + // BOOT_COMPLETE after factory reset. + FACTORY_RESET_BOOT_COMPLETE = 6; + // BOOT_COMPLETE_NO_ENCRYPTION after factory reset. + FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION = 7; + // BOOT_COMPLETE_POST_DESCRYPT after factory reset. + FACTORY_RESET_BOOT_COMPLETE_POST_DESCRYPT = 8; + // BOOT_COMPLETE after OTA. + OTA_BOOT_COMPLETE = 9; + // BOOT_COMPLETE_NO_ENCRYPTION after OTA. + OTA_BOOT_COMPLETE_NO_ENCRYPTION = 10; + // BOOT_COMPLETE_POST_DESCRYPT after OTA. + OTA_BOOT_COMPLETE_POST_DESCRYPT = 11; + // Time when the system starts sending LOCKED_BOOT_COMPLETED broadcast. + // Logged from f/b/services/.../UserController.java + FRAMEWORK_LOCKED_BOOT_COMPLETED = 12; + // Time when the system starts sending BOOT_COMPLETED broadcast. + // Logged from f/b/services/.../UserController.java + FRAMEWORK_BOOT_COMPLETED = 13; + // Time when the package manager starts init. + // Logged from f/b/services/.../SystemServer.java + PACKAGE_MANAGER_INIT_START = 14; + // Time when package manager is ready + // Logged from f/b/services/.../SystemServer.java + PACKAGE_MANAGER_INIT_READY = 15; + // Represents the time when user has entered unlock credential for system with user pin. + // Logged from bootstat. + POST_DECRYPT = 16; + // Represents the start of zygote's init. + // Logged from zygote itself. + ZYGOTE_INIT_START = 17; + // Represents the start of secondary zygote's init. + // TODO: add logging to zygote + SECONDARY_ZYGOTE_INIT_START = 18; + // Represents the start of system server's init. + // Logged from f/b/services/.../SystemServer.java + SYSTEM_SERVER_INIT_START = 19; + // Represents the completion of system server's init. + // Logged from f/b/services/.../SystemServer.java + SYSTEM_SERVER_READY = 20; + // Represents the start of launcher during boot-up. + // TODO: add logging + LAUNCHER_START = 21; + // Represents the completion of launcher's initial rendering. User can use other apps from + // launcher from this point. + // TODO: add logging + LAUNCHER_SHOWN = 22; + } + + // Type of the event. + optional ElapsedTimeEvent event = 1; + // Time since bootup for the event. + // It should be acquired from SystemClock elapsedRealtime() call or equivalent. + optional int64 time_millis = 2; +} + +/** + * Boot time events with UTC time. + * + * Logged from: bootstat and various system server components. Check each enums for details. + */ +message BootTimeEventUtcTime { + enum UtcTimeEvent { + UNKNOWN = 0; + // Time of the bootstat's marking of 1st boot after the last factory reset. + // Logged from bootstat. + FACTORY_RESET_RESET_TIME = 1; + // The time when bootstat records FACTORY_RESET_* events. This is close to + // BOOT_COMPLETE time for the current bootup. + // Logged from bootstat. + FACTORY_RESET_CURRENT_TIME = 2; + // DUplicate of FACTORY_RESET_RESET_TIME added for debugging purpose. + // Logged from bootstat. + FACTORY_RESET_RECORD_VALUE = 3; + } + + // Type of the event. + optional UtcTimeEvent event = 1; + // UTC time for the event. + optional int64 utc_time_secs = 2; +} + +/** + * Boot time events representing specific error code during bootup. + * Meaning of error code can be different per each event type. + * + * Logged from: bootstat and various system server components. Check each enums for details. + */ +message BootTimeEventErrorCode { + enum ErrorCodeEvent { + UNKNOWN = 0; + // Linux error code for time() call to get the current UTC time. + // Logged from bootstat. + FACTORY_RESET_CURRENT_TIME_FAILURE = 1; + // Represents UmountStat before the reboot for the current boot up. Error codes defined + // as UMOUNT_STAT_* from init/reboot.cpp. + // Logged from f/b/services/.../BootReceiver.java. + SHUTDOWN_UMOUNT_STAT = 2; + // Reprepsents fie system mounting error code for the current boot. Error codes defined + // as combination of FsStatFlags from system/core/fs_mgr/fs_mgr.cpp. + // Logged from f/b/services/.../BootReceiver.java. + FS_MGR_FS_STAT = 3; + } + + // Type of the event. + optional ErrorCodeEvent event = 1; + // error code defined per each event type. + // For example, this can have a value of FsStatFlags.FS_STAT_FULL_MOUNT_FAILED for the event of + // FS_MGR_FS_STAT. + optional int32 error_code = 2; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -4725,36 +4932,69 @@ message RoleHolder { } message AggStats { - optional int64 min = 1; - - optional int64 average = 2; - - optional int64 max = 3; -} - + // These are all in byte resolution. + optional int64 min = 1 [deprecated = true]; + optional int64 average = 2 [deprecated = true]; + optional int64 max = 3 [deprecated = true]; + + // These are all in kilobyte resolution. Can fit in int32, so smaller on the wire than the above + // int64 fields. + optional int32 mean_kb = 4; + optional int32 max_kb = 5; +} + +// A reduced subset of process states; reducing the number of possible states allows more +// aggressive device-side aggregation of statistics and hence reduces metric upload size. +enum ProcessStateAggregated { + PROCESS_STATE_UNKNOWN = 0; + // Persistent system process. + PROCESS_STATE_PERSISTENT = 1; + // Top activity; actually any visible activity. + PROCESS_STATE_TOP = 2; + // Process binding to top or a foreground service. + PROCESS_STATE_BOUND_TOP_OR_FGS = 3; + // Processing running a foreground service. + PROCESS_STATE_FGS = 4; + // Important foreground process (ime, wallpaper, etc). + PROCESS_STATE_IMPORTANT_FOREGROUND = 5; + // Important background process. + PROCESS_STATE_BACKGROUND = 6; + // Process running a receiver. + PROCESS_STATE_RECEIVER = 7; + // All kinds of cached processes. + PROCESS_STATE_CACHED = 8; +} + +// Next tag: 13 message ProcessStatsStateProto { optional android.service.procstats.ScreenState screen_state = 1; - optional android.service.procstats.MemoryState memory_state = 2; + optional android.service.procstats.MemoryState memory_state = 2 [deprecated = true]; // this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java // and not frameworks/base/core/java/android/app/ActivityManager.java - optional android.service.procstats.ProcessState process_state = 3; + optional android.service.procstats.ProcessState process_state = 3 [deprecated = true]; + + optional ProcessStateAggregated process_state_aggregated = 10; // Millisecond uptime duration spent in this state - optional int64 duration_millis = 4; + optional int64 duration_millis = 4 [deprecated = true]; + // Same as above, but with minute resolution so it fits into an int32. + optional int32 duration_minutes = 11; // Millisecond elapsed realtime duration spent in this state - optional int64 realtime_duration_millis = 9; + optional int64 realtime_duration_millis = 9 [deprecated = true]; + // Same as above, but with minute resolution so it fits into an int32. + optional int32 realtime_duration_minutes = 12; // # of samples taken optional int32 sample_size = 5; // PSS is memory reserved for this process - optional AggStats pss = 6; + optional AggStats pss = 6 [deprecated = true]; // USS is memory shared between processes, divided evenly for accounting - optional AggStats uss = 7; + optional AggStats uss = 7 [deprecated = true]; // RSS is memory resident for this process optional AggStats rss = 8; @@ -4779,7 +5019,7 @@ message ProcessStatsProto { // PSS stats during cached kill optional AggStats cached_pss = 3; } - optional Kill kill = 3; + optional Kill kill = 3 [deprecated = true]; // Time and memory spent in various states. repeated ProcessStatsStateProto states = 5; diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp index ef59c9242cb2..3ad21e0c96ae 100644 --- a/cmds/statsd/src/state/StateTracker.cpp +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -28,10 +28,14 @@ namespace statsd { StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo) : mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) { // create matcher for each primary field - // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain - for (const auto& primary : stateAtomInfo.primaryFields) { - Matcher matcher = getSimpleMatcher(atomId, primary); - mPrimaryFields.push_back(matcher); + for (const auto& primaryField : stateAtomInfo.primaryFields) { + if (primaryField == util::FIRST_UID_IN_CHAIN) { + Matcher matcher = getFirstUidMatcher(atomId); + mPrimaryFields.push_back(matcher); + } else { + Matcher matcher = getSimpleMatcher(atomId, primaryField); + mPrimaryFields.push_back(matcher); + } } // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h index 7453370f25fd..70f16274c7f6 100644 --- a/cmds/statsd/src/state/StateTracker.h +++ b/cmds/statsd/src/state/StateTracker.h @@ -72,7 +72,7 @@ private: int32_t mDefaultState = kStateUnknown; - int32_t mResetState; + int32_t mResetState = kStateUnknown; // Maps primary key to state value info std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap; diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index 26a3733ed598..84aaa54bc5bf 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -76,6 +76,23 @@ std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) { return event; } +// State with first uid in attribution chain as primary field - WakelockStateChanged +std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) { + std::vector<AttributionNodeInternal> chain; + chain.push_back(AttributionNodeInternal()); + AttributionNodeInternal& attr = chain.back(); + attr.set_uid(uid); + + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */); + event->write(chain); + event->write((int32_t)1); // PARTIAL_WAKE_LOCK + event->write(tag); + event->write(acquire ? 1 : 0); + event->init(); + return event; +} + // State with multiple primary fields - OverlayStateChanged std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) { std::shared_ptr<LogEvent> event = @@ -134,6 +151,39 @@ void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) { key->addValue(FieldValue(field1, value1)); key->addValue(FieldValue(field2, value2)); } + +void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) { + int pos1[] = {1, 1, 1}; + int pos3[] = {2, 0, 0}; + int pos4[] = {3, 0, 0}; + + Field field1(10 /* atom id */, pos1, 2 /* depth */); + + Field field3(10 /* atom id */, pos3, 0 /* depth */); + Field field4(10 /* atom id */, pos4, 0 /* depth */); + + Value value1((int32_t)uid); + Value value3((int32_t)1 /*partial*/); + Value value4(tag); + + key->addValue(FieldValue(field1, value1)); + key->addValue(FieldValue(field3, value3)); + key->addValue(FieldValue(field4, value4)); +} + +void getPartialWakelockKey(int uid, HashableDimensionKey* key) { + int pos1[] = {1, 1, 1}; + int pos3[] = {2, 0, 0}; + + Field field1(10 /* atom id */, pos1, 2 /* depth */); + Field field3(10 /* atom id */, pos3, 0 /* depth */); + + Value value1((int32_t)uid); + Value value3((int32_t)1 /*partial*/); + + key->addValue(FieldValue(field1, value1)); + key->addValue(FieldValue(field3, value3)); +} // END: get primary key functions TEST(StateListenerTest, TestStateListenerWeakPointer) { @@ -247,7 +297,8 @@ TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { // check StateTracker was updated by querying for state HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey)); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey)); } /** @@ -272,7 +323,46 @@ TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { // check StateTracker was updated by querying for state HashableDimensionKey queryKey; getUidProcessKey(1000 /* uid */, &queryKey); - EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey)); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, + getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey)); +} + +TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener1); + + // Log event. + std::shared_ptr<LogEvent> event = + buildPartialWakelockEvent(1001 /* uid */, "tag1", false /* acquire */); + mgr.onLogEvent(*event); + + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::WAKELOCK_STATE_CHANGED)); + + // Check listener was updated. + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size()); + EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value); + EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value); + EXPECT_EQ(WakelockStateChanged::RELEASE, listener1->updates[0].mState); + + // Check StateTracker was updated by querying for state. + HashableDimensionKey queryKey; + getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey)); + + // No state stored for this query key. + HashableDimensionKey queryKey2; + getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); + EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2)); + + // Partial query fails. + HashableDimensionKey queryKey3; + getPartialWakelockKey(1001 /* uid */, &queryKey3); + EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3)); } /** @@ -297,7 +387,8 @@ TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { // check StateTracker was updated by querying for state HashableDimensionKey queryKey; getOverlayKey(1000 /* uid */, "package1", &queryKey); - EXPECT_EQ(1, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey)); + EXPECT_EQ(OverlayStateChanged::ENTERED, + getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey)); } /** @@ -326,10 +417,12 @@ TEST(StateTrackerTest, TestStateQuery) { sp<TestStateListener> listener1 = new TestStateListener(); sp<TestStateListener> listener2 = new TestStateListener(); sp<TestStateListener> listener3 = new TestStateListener(); + sp<TestStateListener> listener4 = new TestStateListener(); StateManager mgr; mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2); mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3); + mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener4); std::shared_ptr<LogEvent> event1 = buildUidProcessEvent( 1000, @@ -346,8 +439,12 @@ TEST(StateTrackerTest, TestStateQuery) { android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 std::shared_ptr<LogEvent> event5 = buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); - std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1); - std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2); + std::shared_ptr<LogEvent> event6 = + buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED); + std::shared_ptr<LogEvent> event7 = + buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED); + std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true); + std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false); mgr.onLogEvent(*event1); mgr.onLogEvent(*event2); @@ -356,11 +453,14 @@ TEST(StateTrackerTest, TestStateQuery) { mgr.onLogEvent(*event5); mgr.onLogEvent(*event6); mgr.onLogEvent(*event7); + mgr.onLogEvent(*event8); + mgr.onLogEvent(*event9); // Query for UidProcessState of uid 1001 HashableDimensionKey queryKey1; getUidProcessKey(1001, &queryKey1); - EXPECT_EQ(1003, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, + getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); // Query for UidProcessState of uid 1004 - not in state map HashableDimensionKey queryKey2; @@ -370,15 +470,30 @@ TEST(StateTrackerTest, TestStateQuery) { // Query for UidProcessState of uid 1001 - after change in state mgr.onLogEvent(*event4); - EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, + getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); // Query for ScreenState - EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); // Query for OverlayState of uid 1000, package name "package2" HashableDimensionKey queryKey3; getOverlayKey(1000, "package2", &queryKey3); - EXPECT_EQ(2, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3)); + EXPECT_EQ(OverlayStateChanged::EXITED, + getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3)); + + // Query for WakelockState of uid 1005, tag 2 + HashableDimensionKey queryKey4; + getPartialWakelockKey(1005, "tag2", &queryKey4); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey4)); + + // Query for WakelockState of uid 1005, tag 1 + HashableDimensionKey queryKey5; + getPartialWakelockKey(1005, "tag1", &queryKey5); + EXPECT_EQ(WakelockStateChanged::ACQUIRE, + getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey5)); } } // namespace statsd diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index c0fee6e9e5e9..e46840c0f467 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -1547,6 +1547,34 @@ public abstract class AccessibilityService extends Service { void onShowModeChanged(@NonNull SoftKeyboardController controller, @SoftKeyboardShowMode int showMode); } + + /** + * Switches the current IME for the user for whom the service is enabled. The change will + * persist until the current IME is explicitly changed again, and may persist beyond the + * life cycle of the requesting service. + * + * @param imeId The ID of the input method to make current. This IME must be installed and + * enabled. + * @return {@code true} if the current input method was successfully switched to the input + * method by {@code imeId}, + * {@code false} if the input method specified is not installed, not enabled, or + * otherwise not available to become the current IME + * + * @see android.view.inputmethod.InputMethodInfo#getId() + */ + public boolean switchToInputMethod(@NonNull String imeId) { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection( + mService.mConnectionId); + if (connection != null) { + try { + return connection.switchToInputMethod(imeId); + } catch (RemoteException re) { + throw new RuntimeException(re); + } + } + return false; + } } /** diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 4841781170e1..656f87fe435b 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -91,6 +91,8 @@ interface IAccessibilityServiceConnection { void setSoftKeyboardCallbackEnabled(boolean enabled); + boolean switchToInputMethod(String imeId); + boolean isAccessibilityButtonAvailable(); void sendGesture(int sequence, in ParceledListSlice gestureSteps); diff --git a/core/java/android/annotation/SystemService.java b/core/java/android/annotation/SystemService.java index 0c5d15e178a3..c05c1bab06d2 100644 --- a/core/java/android/annotation/SystemService.java +++ b/core/java/android/annotation/SystemService.java @@ -19,14 +19,12 @@ package android.annotation; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.content.Context; - import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Description of a system service available through - * {@link Context#getSystemService(Class)}. This is used to auto-generate + * {@link android.content.Context#getSystemService(Class)}. This is used to auto-generate * documentation explaining how to obtain a reference to the service. * * @hide @@ -36,9 +34,9 @@ import java.lang.annotation.Target; public @interface SystemService { /** * The string name of the system service that can be passed to - * {@link Context#getSystemService(String)}. + * {@link android.content.Context#getSystemService(String)}. * - * @see Context#getSystemServiceName(Class) + * @see android.content.Context#getSystemServiceName(Class) */ String value(); } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index aa115984b5e1..d23754e9d433 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -482,19 +482,6 @@ public class ResourcesManager { } } - if (key.mOverlayDirs != null) { - for (final String idmapPath : key.mOverlayDirs) { - try { - builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, - true /*overlay*/)); - } catch (IOException e) { - Log.w(TAG, "failed to add overlay path " + idmapPath); - - // continue. - } - } - } - if (key.mLibDirs != null) { for (final String libDir : key.mLibDirs) { if (libDir.endsWith(".apk")) { @@ -513,6 +500,19 @@ public class ResourcesManager { } } + if (key.mOverlayDirs != null) { + for (final String idmapPath : key.mOverlayDirs) { + try { + builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, + true /*overlay*/)); + } catch (IOException e) { + Log.w(TAG, "failed to add overlay path " + idmapPath); + + // continue. + } + } + } + return builder.build(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index ca3d0d7065c9..a9be9ea33089 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -170,6 +170,7 @@ import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; import android.service.vr.IVrManager; import android.telecom.TelecomManager; +import android.telephony.MmsManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyRegistryManager; import android.util.ArrayMap; @@ -345,6 +346,14 @@ public final class SystemServiceRegistry { } }); + registerService(Context.NETWORK_STACK_SERVICE, IBinder.class, + new StaticServiceFetcher<IBinder>() { + @Override + public IBinder createService() { + return ServiceManager.getService(Context.NETWORK_STACK_SERVICE); + } + }); + registerService(Context.TETHERING_SERVICE, TetheringManager.class, new CachedServiceFetcher<TetheringManager>() { @Override @@ -631,6 +640,13 @@ public final class SystemServiceRegistry { return new TelecomManager(ctx.getOuterContext()); }}); + registerService(Context.MMS_SERVICE, MmsManager.class, + new CachedServiceFetcher<MmsManager>() { + @Override + public MmsManager createService(ContextImpl ctx) { + return new MmsManager(ctx.getOuterContext()); + }}); + registerService(Context.UI_MODE_SERVICE, UiModeManager.class, new CachedServiceFetcher<UiModeManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 62b499ceb941..69640b8321d1 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1411,6 +1411,16 @@ public class DevicePolicyManager { = "android.app.action.DEVICE_OWNER_CHANGED"; /** + * Broadcast action: sent when the factory reset protection (FRP) policy is changed. + * + * @see #setFactoryResetProtectionPolicy + * @hide + */ + @SystemApi + public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = + "android.app.action.RESET_PROTECTION_POLICY_CHANGED"; + + /** * The ComponentName of the administrator component. * * @see #ACTION_ADD_DEVICE_ADMIN @@ -4329,6 +4339,60 @@ public class DevicePolicyManager { } /** + * Callable by device owner or profile owner of an organization-owned device, to set a + * factory reset protection (FRP) policy. When a new policy is set, the system + * notifies the FRP management agent of a policy change by broadcasting + * {@code ACTION_RESET_PROTECTION_POLICY_CHANGED}. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param policy the new FRP policy, or {@code null} to clear the current policy. + * @throws SecurityException if {@code admin} is not a device owner or a profile owner of + * an organization-owned device. + * @throws UnsupportedOperationException if factory reset protection is not + * supported on the device. + */ + public void setFactoryResetProtectionPolicy(@NonNull ComponentName admin, + @Nullable FactoryResetProtectionPolicy policy) { + throwIfParentInstance("setFactoryResetProtectionPolicy"); + if (mService != null) { + try { + mService.setFactoryResetProtectionPolicy(admin, policy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Callable by device owner or profile owner of an organization-owned device, to retrieve + * the current factory reset protection (FRP) policy set previously by + * {@link #setFactoryResetProtectionPolicy}. + * <p> + * This method can also be called by the FRP management agent on device, in which case, + * it can pass {@code null} as the ComponentName. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with or + * {@code null} if called by the FRP management agent on device. + * @return The current FRP policy object or {@code null} if no policy is set. + * @throws SecurityException if {@code admin} is not a device owner, a profile owner of + * an organization-owned device or the FRP management agent. + * @throws UnsupportedOperationException if factory reset protection is not + * supported on the device. + */ + public @Nullable FactoryResetProtectionPolicy getFactoryResetProtectionPolicy( + @Nullable ComponentName admin) { + throwIfParentInstance("getFactoryResetProtectionPolicy"); + if (mService != null) { + try { + return mService.getFactoryResetProtectionPolicy(admin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return null; + } + + /** * Called by an application that is administering the device to set the * global proxy and exclusion list. * <p> diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl b/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl new file mode 100644 index 000000000000..72e639a76d18 --- /dev/null +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl @@ -0,0 +1,19 @@ +/* + * 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.app.admin; + +parcelable FactoryResetProtectionPolicy; diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java new file mode 100644 index 000000000000..ed7477936f9c --- /dev/null +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java @@ -0,0 +1,237 @@ +/* + * 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.app.admin; + +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.TEXT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * The factory reset protection policy determines which accounts can unlock a device that + * has gone through untrusted factory reset. + * <p> + * Only a device owner or profile owner of an organization-owned device can set a factory + * reset protection policy for the device by calling the {@code DevicePolicyManager} method + * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName, + * FactoryResetProtectionPolicy)}}. + * + * @see DevicePolicyManager#setFactoryResetProtectionPolicy + * @see DevicePolicyManager#getFactoryResetProtectionPolicy + */ +public final class FactoryResetProtectionPolicy implements Parcelable { + + private static final String LOG_TAG = "FactoryResetProtectionPolicy"; + + private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT = + "factory_reset_protection_account"; + private static final String KEY_FACTORY_RESET_PROTECTION_DISABLED = + "factory_reset_protection_disabled"; + private static final String ATTR_VALUE = "value"; + + private final List<String> mFactoryResetProtectionAccounts; + private final boolean mFactoryResetProtectionDisabled; + + private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts, + boolean factoryResetProtectionDisabled) { + mFactoryResetProtectionAccounts = factoryResetProtectionAccounts; + mFactoryResetProtectionDisabled = factoryResetProtectionDisabled; + } + + /** + * Get the list of accounts that can provision a device which has been factory reset. + */ + public @NonNull List<String> getFactoryResetProtectionAccounts() { + return mFactoryResetProtectionAccounts; + } + + /** + * Return whether factory reset protection for the device is disabled or not. + */ + public boolean isFactoryResetProtectionDisabled() { + return mFactoryResetProtectionDisabled; + } + + /** + * Builder class for {@link FactoryResetProtectionPolicy} objects. + */ + public static class Builder { + private List<String> mFactoryResetProtectionAccounts; + private boolean mFactoryResetProtectionDisabled; + + /** + * Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}. + */ + public Builder() { + }; + + /** + * Sets which accounts can unlock a device that has been factory reset. + * <p> + * Once set, the consumer unlock flow will be disabled and only accounts in this list + * can unlock factory reset protection after untrusted factory reset. + * <p> + * It's up to the FRP management agent to interpret the {@code String} as account it + * supports. Please consult their relevant documentation for details. + * + * @param factoryResetProtectionAccounts list of accounts. + * @return the same Builder instance. + */ + @NonNull + public Builder setFactoryResetProtectionAccounts( + @NonNull List<String> factoryResetProtectionAccounts) { + mFactoryResetProtectionAccounts = new ArrayList<>(factoryResetProtectionAccounts); + return this; + } + + /** + * Sets whether factory reset protection is disabled or not. + * <p> + * Once disabled, factory reset protection will not kick in all together when the device + * goes through untrusted factory reset. This applies to both the consumer unlock flow and + * the admin account overrides via {@link #setFactoryResetProtectionAccounts} + * + * @param factoryResetProtectionDisabled Whether the policy is disabled or not. + * @return the same Builder instance. + */ + @NonNull + public Builder setFactoryResetProtectionDisabled(boolean factoryResetProtectionDisabled) { + mFactoryResetProtectionDisabled = factoryResetProtectionDisabled; + return this; + } + + /** + * Combines all of the attributes that have been set on this {@code Builder} + * + * @return a new {@link FactoryResetProtectionPolicy} object. + */ + @NonNull + public FactoryResetProtectionPolicy build() { + return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts, + mFactoryResetProtectionDisabled); + } + } + + @Override + public String toString() { + return "FactoryResetProtectionPolicy{" + + "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts + + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled + + '}'; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, @Nullable int flags) { + int accountsCount = mFactoryResetProtectionAccounts.size(); + dest.writeInt(accountsCount); + for (String account: mFactoryResetProtectionAccounts) { + dest.writeString(account); + } + dest.writeBoolean(mFactoryResetProtectionDisabled); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<FactoryResetProtectionPolicy> CREATOR = + new Creator<FactoryResetProtectionPolicy>() { + + @Override + public FactoryResetProtectionPolicy createFromParcel(Parcel in) { + List<String> factoryResetProtectionAccounts = new ArrayList<>(); + int accountsCount = in.readInt(); + for (int i = 0; i < accountsCount; i++) { + factoryResetProtectionAccounts.add(in.readString()); + } + boolean factoryResetProtectionDisabled = in.readBoolean(); + + return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts, + factoryResetProtectionDisabled); + } + + @Override + public FactoryResetProtectionPolicy[] newArray(int size) { + return new FactoryResetProtectionPolicy[size]; + } + }; + + /** + * Restore a previously saved FactoryResetProtectionPolicy from XML. + * <p> + * No validation is required on the reconstructed policy since the XML was previously + * created by the system server from a validated policy. + * @hide + */ + @Nullable + public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) { + try { + boolean factoryResetProtectionDisabled = Boolean.parseBoolean( + parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_DISABLED)); + + List<String> factoryResetProtectionAccounts = new ArrayList<>(); + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != END_DOCUMENT + && (type != END_TAG || parser.getDepth() > outerDepth)) { + if (type == END_TAG || type == TEXT) { + continue; + } + if (!parser.getName().equals(KEY_FACTORY_RESET_PROTECTION_ACCOUNT)) { + continue; + } + factoryResetProtectionAccounts.add( + parser.getAttributeValue(null, ATTR_VALUE)); + } + + return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts, + factoryResetProtectionDisabled); + } catch (XmlPullParserException | IOException e) { + Log.w(LOG_TAG, "Reading from xml failed", e); + } + return null; + } + + /** + * @hide + */ + public void writeToXml(@NonNull XmlSerializer out) throws IOException { + out.attribute(null, KEY_FACTORY_RESET_PROTECTION_DISABLED, + Boolean.toString(mFactoryResetProtectionDisabled)); + for (String account : mFactoryResetProtectionAccounts) { + out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT); + out.attribute(null, ATTR_VALUE, account); + out.endTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT); + } + } + +} diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3eec46bd010e..21c9eb5c60ad 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -24,6 +24,7 @@ import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.admin.PasswordMetrics; +import android.app.admin.FactoryResetProtectionPolicy; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -104,6 +105,9 @@ interface IDevicePolicyManager { void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent); + void setFactoryResetProtectionPolicy(in ComponentName who, in FactoryResetProtectionPolicy policy); + FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(in ComponentName who); + ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); ComponentName getGlobalProxyAdmin(int userHandle); void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo); diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 4346d9738ec7..9c4a8f4fbe27 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -16,7 +16,10 @@ package android.app.usage; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.usage.NetworkStats.Bucket; @@ -27,6 +30,9 @@ import android.net.DataUsageRequest; import android.net.INetworkStatsService; import android.net.NetworkIdentity; import android.net.NetworkTemplate; +import android.net.netstats.provider.AbstractNetworkStatsProvider; +import android.net.netstats.provider.NetworkStatsProviderCallback; +import android.net.netstats.provider.NetworkStatsProviderWrapper; import android.os.Binder; import android.os.Handler; import android.os.Looper; @@ -519,6 +525,34 @@ public class NetworkStatsManager { private DataUsageRequest request; } + /** + * Registers a custom provider of {@link android.net.NetworkStats} to combine the network + * statistics that cannot be seen by the kernel to system. To unregister, invoke + * {@link NetworkStatsProviderCallback#unregister()}. + * + * @param tag a human readable identifier of the custom network stats provider. + * @param provider a custom implementation of {@link AbstractNetworkStatsProvider} that needs to + * be registered to the system. + * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the + * system. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) + @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider( + @NonNull String tag, + @NonNull AbstractNetworkStatsProvider provider) { + try { + final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider); + return new NetworkStatsProviderCallback( + mService.registerNetworkStatsProvider(tag, wrapper)); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + // Unreachable code, but compiler doesn't know about it. + return null; + } + private static NetworkTemplate createTemplate(int networkType, String subscriberId) { final NetworkTemplate template; switch (networkType) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 44b2df691876..9ef95743b0c9 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3416,6 +3416,7 @@ public abstract class Context { TELEPHONY_SUBSCRIPTION_SERVICE, CARRIER_CONFIG_SERVICE, EUICC_SERVICE, + MMS_SERVICE, TELECOM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, @@ -3612,6 +3613,8 @@ public abstract class Context { * @see android.telephony.CarrierConfigManager * @see #EUICC_SERVICE * @see android.telephony.euicc.EuiccManager + * @see #MMS_SERVICE + * @see android.telephony.MmsManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE @@ -3950,10 +3953,12 @@ public abstract class Context { /** * Use with {@link android.os.ServiceManager.getService()} to retrieve a - * {@link NetworkStackClient} IBinder for communicating with the network stack + * {@link INetworkStackConnector} IBinder for communicating with the network stack * @hide * @see NetworkStackClient */ + @SystemApi + @TestApi public static final String NETWORK_STACK_SERVICE = "network_stack"; /** @@ -4288,6 +4293,15 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.MmsManager} to send/receive MMS messages. + * + * @see #getSystemService(String) + * @see android.telephony.MmsManager + */ + public static final String MMS_SERVICE = "mms"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.content.ClipboardManager} for accessing and modifying * the contents of the global clipboard. * diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 32803ab8f859..87acbc146f49 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -7803,7 +7803,7 @@ public class PackageParser { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.overlayPaths; + ai.resourceDirs = state.getAllOverlayPaths(); ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index f0f6753fc93e..da7623a1426e 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -46,6 +46,8 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.Objects; /** @@ -77,7 +79,9 @@ public class PackageUserState { public ArraySet<String> disabledComponents; public ArraySet<String> enabledComponents; - public String[] overlayPaths; + private String[] overlayPaths; + private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths + private String[] cachedOverlayPaths; @UnsupportedAppUsage public PackageUserState() { @@ -112,9 +116,33 @@ public class PackageUserState { enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); overlayPaths = o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length); + if (o.sharedLibraryOverlayPaths != null) { + sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths); + } harmfulAppWarning = o.harmfulAppWarning; } + public String[] getOverlayPaths() { + return overlayPaths; + } + + public void setOverlayPaths(String[] paths) { + overlayPaths = paths; + cachedOverlayPaths = null; + } + + public Map<String, String[]> getSharedLibraryOverlayPaths() { + return sharedLibraryOverlayPaths; + } + + public void setSharedLibraryOverlayPaths(String library, String[] paths) { + if (sharedLibraryOverlayPaths == null) { + sharedLibraryOverlayPaths = new ArrayMap<>(); + } + sharedLibraryOverlayPaths.put(library, paths); + cachedOverlayPaths = null; + } + /** * Test if this package is installed. */ @@ -235,6 +263,38 @@ public class PackageUserState { return isComponentEnabled; } + public String[] getAllOverlayPaths() { + if (overlayPaths == null && sharedLibraryOverlayPaths == null) { + return null; + } + + if (cachedOverlayPaths != null) { + return cachedOverlayPaths; + } + + final LinkedHashSet<String> paths = new LinkedHashSet<>(); + if (overlayPaths != null) { + final int N = overlayPaths.length; + for (int i = 0; i < N; i++) { + paths.add(overlayPaths[i]); + } + } + + if (sharedLibraryOverlayPaths != null) { + for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) { + if (libOverlayPaths != null) { + final int N = libOverlayPaths.length; + for (int i = 0; i < N; i++) { + paths.add(libOverlayPaths[i]); + } + } + } + } + + cachedOverlayPaths = paths.toArray(new String[0]); + return cachedOverlayPaths; + } + @Override final public boolean equals(Object obj) { if (!(obj instanceof PackageUserState)) { diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java index f2cf9a484c6e..73a8d2a7dc0f 100644 --- a/core/java/android/content/pm/parsing/PackageInfoUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java @@ -41,9 +41,11 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; +import android.util.ArraySet; import com.android.internal.util.ArrayUtils; +import java.util.LinkedHashSet; import java.util.Set; /** @hide */ @@ -545,7 +547,7 @@ public class PackageInfoUtils { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.overlayPaths; + ai.resourceDirs = state.getAllOverlayPaths(); ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } diff --git a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java index 07d2443ffacb..6ead64c02ef9 100644 --- a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java @@ -25,9 +25,12 @@ import java.util.List; * A constrained high speed capture session for a {@link CameraDevice}, used for capturing high * speed images from the {@link CameraDevice} for high speed video recording use case. * <p> - * A CameraHighSpeedCaptureSession is created by providing a set of target output surfaces to - * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, Once created, the session is - * active until a new session is created by the camera device, or the camera device is closed. + * A CameraConstrainedHighSpeedCaptureSession is created by providing a session configuration to + * {@link CameraDevice#createCaptureSession(SessionConfiguration)} with a type of + * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_HIGH_SPEED}. The + * CameraCaptureSession returned from {@link CameraCaptureSession.StateCallback} can then be cast to + * a CameraConstrainedHighSpeedCaptureSession. Once created, the session is active until a new + * session is created by the camera device, or the camera device is closed. * </p> * <p> * An active high speed capture session is a specialized capture session that is only targeted at @@ -37,8 +40,8 @@ import java.util.List; * accepts request lists created via {@link #createHighSpeedRequestList}, and the request list can * only be submitted to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}. See - * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} for more details of the - * limitations. + * {@link CameraDevice#createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} + * for more details of the limitations. * </p> * <p> * Creating a session is an expensive operation and can take several hundred milliseconds, since it @@ -50,13 +53,6 @@ import java.util.List; * completed, then the {@link CameraCaptureSession.StateCallback#onConfigureFailed} is called, and * the session will not become active. * </p> - * <!-- - * <p> - * Any capture requests (repeating or non-repeating) submitted before the session is ready will be - * queued up and will begin capture once the session becomes ready. In case the session cannot be - * configured and {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is - * called, all queued capture requests are discarded. </p> - * --> * <p> * If a new session is created by the camera device, then the previous session is closed, and its * associated {@link CameraCaptureSession.StateCallback#onClosed onClosed} callback will be diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index fb1ece29a369..cc066812ba1f 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -16,24 +16,22 @@ package android.hardware.camera2; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; -import static android.hardware.camera2.ICameraDeviceUser.NORMAL_MODE; -import static android.hardware.camera2.ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE; import android.hardware.camera2.params.InputConfiguration; -import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.StreamConfigurationMap; import android.os.Handler; import android.view.Surface; -import java.util.List; -import java.util.Set; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Set; /** * <p>The CameraDevice class is a representation of a single camera connected to an @@ -220,6 +218,224 @@ public abstract class CameraDevice implements AutoCloseable { * <p>Create a new camera capture session by providing the target output set of Surfaces to the * camera device.</p> * + * @param outputs The new set of Surfaces that should be made available as + * targets for captured image data. + * @param callback The callback to notify about the status of the new capture session. + * @param handler The handler on which the callback should be invoked, or {@code null} to use + * the current thread's {@link android.os.Looper looper}. + * + * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements, + * the callback is null, or the handler is null but the current + * thread has no looper. + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + * + * @see CameraCaptureSession + * @see StreamConfigurationMap#getOutputFormats() + * @see StreamConfigurationMap#getOutputSizes(int) + * @see StreamConfigurationMap#getOutputSizes(Class) + * @deprecated Please use @{link + * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the + * full set of configuration options available. + */ + @Deprecated + public abstract void createCaptureSession(@NonNull List<Surface> outputs, + @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) + throws CameraAccessException; + + /** + * <p>Create a new camera capture session by providing the target output set of Surfaces and + * its corresponding surface configuration to the camera device.</p> + * + * @see #createCaptureSession + * @see OutputConfiguration + * @deprecated Please use @{link + * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the + * full set of configuration options available. + */ + @Deprecated + public abstract void createCaptureSessionByOutputConfigurations( + List<OutputConfiguration> outputConfigurations, + CameraCaptureSession.StateCallback callback, @Nullable Handler handler) + throws CameraAccessException; + /** + * Create a new reprocessable camera capture session by providing the desired reprocessing + * input Surface configuration and the target output set of Surfaces to the camera device. + * + * @param inputConfig The configuration for the input {@link Surface} + * @param outputs The new set of Surfaces that should be made available as + * targets for captured image data. + * @param callback The callback to notify about the status of the new capture session. + * @param handler The handler on which the callback should be invoked, or {@code null} to use + * the current thread's {@link android.os.Looper looper}. + * + * @throws IllegalArgumentException if the input configuration is null or not supported, the set + * of output Surfaces do not meet the requirements, the + * callback is null, or the handler is null but the current + * thread has no looper. + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + * + * @see #createCaptureSession + * @see CameraCaptureSession + * @see StreamConfigurationMap#getInputFormats + * @see StreamConfigurationMap#getInputSizes + * @see StreamConfigurationMap#getValidOutputFormatsForInput + * @see StreamConfigurationMap#getOutputSizes + * @see android.media.ImageWriter + * @see android.media.ImageReader + * @deprecated Please use @{link + * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the + * full set of configuration options available. + */ + @Deprecated + public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig, + @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException; + + /** + * Create a new reprocessable camera capture session by providing the desired reprocessing + * input configuration and output {@link OutputConfiguration} + * to the camera device. + * + * @see #createReprocessableCaptureSession + * @see OutputConfiguration + * @deprecated Please use @{link + * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the + * full set of configuration options available. + */ + @Deprecated + public abstract void createReprocessableCaptureSessionByConfigurations( + @NonNull InputConfiguration inputConfig, + @NonNull List<OutputConfiguration> outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException; + + /** + * <p>Create a new constrained high speed capture session.</p> + * + * @param outputs The new set of Surfaces that should be made available as + * targets for captured high speed image data. + * @param callback The callback to notify about the status of the new capture session. + * @param handler The handler on which the callback should be invoked, or {@code null} to use + * the current thread's {@link android.os.Looper looper}. + * + * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements, + * the callback is null, or the handler is null but the current + * thread has no looper, or the camera device doesn't support + * high speed video capability. + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + * + * @see #createCaptureSession + * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE + * @see StreamConfigurationMap#getHighSpeedVideoSizes + * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO + * @see CameraCaptureSession#captureBurst + * @see CameraCaptureSession#setRepeatingBurst + * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList + * @deprecated Please use @{link + * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the + * full set of configuration options available. + */ + @Deprecated + public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException; + + /** + * Standard camera operation mode. + * + * @see #createCustomCaptureSession + * @hide + */ + @SystemApi + @TestApi + public static final int SESSION_OPERATION_MODE_NORMAL = + 0; // ICameraDeviceUser.NORMAL_MODE; + + /** + * Constrained high-speed operation mode. + * + * @see #createCustomCaptureSession + * @hide + */ + @SystemApi + @TestApi + public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = + 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE; + + /** + * First vendor-specific operating mode + * + * @see #createCustomCaptureSession + * @hide + */ + @SystemApi + @TestApi + public static final int SESSION_OPERATION_MODE_VENDOR_START = + 0x8000; // ICameraDeviceUser.VENDOR_MODE_START; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value = + {SESSION_OPERATION_MODE_NORMAL, + SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED, + SESSION_OPERATION_MODE_VENDOR_START}) + public @interface SessionOperatingMode {}; + + /** + * Create a new camera capture session with a custom operating mode. + * + * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session + * is desired, or {@code null} otherwise. + * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be + * made available as targets for captured image data. + * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom + * vendor value or one of the SESSION_OPERATION_MODE_* values. + * @param callback The callback to notify about the status of the new capture session. + * @param handler The handler on which the callback should be invoked, or {@code null} to use + * the current thread's {@link android.os.Looper looper}. + * + * @throws IllegalArgumentException if the input configuration is null or not supported, the set + * of output Surfaces do not meet the requirements, the + * callback is null, or the handler is null but the current + * thread has no looper. + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + * + * @see #createCaptureSession + * @see #createReprocessableCaptureSession + * @see CameraCaptureSession + * @see OutputConfiguration + * @deprecated Please use @{link + * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the + * full set of configuration options available. + * @hide + */ + @SystemApi + @TestApi + @Deprecated + public abstract void createCustomCaptureSession( + InputConfiguration inputConfig, + @NonNull List<OutputConfiguration> outputs, + @SessionOperatingMode int operatingMode, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException; + + /** + * <p>Create a new {@link CameraCaptureSession} using a {@link SessionConfiguration} helper + * object that aggregates all supported parameters.</p> * <p>The active capture session determines the set of potential output Surfaces for * the camera device for each capture request. A given request may use all * or only some of the outputs. Once the CameraCaptureSession is created, requests can be @@ -308,11 +524,15 @@ public abstract class CameraDevice implements AutoCloseable { * <p>Configuring a session with an empty or null list will close the current session, if * any. This can be used to release the current session's target surfaces for another use.</p> * + * <h3>Regular capture</h3> + * * <p>While any of the sizes from {@link StreamConfigurationMap#getOutputSizes} can be used when * a single output stream is configured, a given camera device may not be able to support all * combination of sizes, formats, and targets when multiple outputs are configured at once. The * tables below list the maximum guaranteed resolutions for combinations of streams and targets, - * given the capabilities of the camera device.</p> + * given the capabilities of the camera device. These are valid for when the + * {@link android.hardware.camera2.params.SessionConfiguration#setInputConfiguration + * input configuration} is not set and therefore no reprocessing is active.</p> * * <p>If an application tries to create a session using a set of targets that exceed the limits * described in the below tables, one of three possibilities may occur. First, the session may @@ -488,56 +708,22 @@ public abstract class CameraDevice implements AutoCloseable { * (either width or height) might not be supported, and capture session creation will fail if it * is not.</p> * - * @param outputs The new set of Surfaces that should be made available as - * targets for captured image data. - * @param callback The callback to notify about the status of the new capture session. - * @param handler The handler on which the callback should be invoked, or {@code null} to use - * the current thread's {@link android.os.Looper looper}. - * - * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements, - * the callback is null, or the handler is null but the current - * thread has no looper. - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera device has been closed - * - * @see CameraCaptureSession - * @see StreamConfigurationMap#getOutputFormats() - * @see StreamConfigurationMap#getOutputSizes(int) - * @see StreamConfigurationMap#getOutputSizes(Class) - */ - public abstract void createCaptureSession(@NonNull List<Surface> outputs, - @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) - throws CameraAccessException; - - /** - * <p>Create a new camera capture session by providing the target output set of Surfaces and - * its corresponding surface configuration to the camera device.</p> - * - * @see #createCaptureSession - * @see OutputConfiguration - */ - public abstract void createCaptureSessionByOutputConfigurations( - List<OutputConfiguration> outputConfigurations, - CameraCaptureSession.StateCallback callback, @Nullable Handler handler) - throws CameraAccessException; - /** - * Create a new reprocessable camera capture session by providing the desired reprocessing - * input Surface configuration and the target output set of Surfaces to the camera device. + * <h3>Reprocessing</h3> * * <p>If a camera device supports YUV reprocessing * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or PRIVATE * reprocessing - * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), besides - * the capture session created via {@link #createCaptureSession createCaptureSession}, the + * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), the * application can also create a reprocessable capture session to submit reprocess capture - * requests in addition to regular capture requests. A reprocess capture request takes the next - * available buffer from the session's input Surface, and sends it through the camera device's - * processing pipeline again, to produce buffers for the request's target output Surfaces. No - * new image data is captured for a reprocess request. However the input buffer provided by - * the application must be captured previously by the same camera device in the same session - * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output - * images).</p> + * requests in addition to regular capture requests, by setting an + * {@link android.hardware.camera2.params.SessionConfiguration#setInputConfiguration + * input configuration} for the session. A reprocess capture request takes the next available + * buffer from the + * session's input Surface, and sends it through the camera device's processing pipeline again, + * to produce buffers for the request's target output Surfaces. No new image data is captured + * for a reprocess request. However the input buffer provided by the application must be + * captured previously by the same camera device in the same session directly (e.g. for + * Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output images).</p> * * <p>The active reprocessable capture session determines an input {@link Surface} and the set * of potential output Surfaces for the camera devices for each capture request. The application @@ -570,10 +756,7 @@ public abstract class CameraDevice implements AutoCloseable { * <p>Starting from API level 30, recreating a reprocessable capture session will flush all the * queued but not yet processed buffers from the input surface.</p> * - * <p>The guaranteed stream configurations listed in - * {@link #createCaptureSession createCaptureSession} are also guaranteed to work for - * {@link #createReprocessableCaptureSession createReprocessableCaptureSession}. In addition, - * the configurations in the tables below are also guaranteed for creating a reprocessable + * <p>The configurations in the tables below are guaranteed for creating a reprocessable * capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing. * However, not all output targets used to create a reprocessable session may be used in a * {@link CaptureRequest} simultaneously. For devices that support only 1 output target in a @@ -602,7 +785,7 @@ public abstract class CameraDevice implements AutoCloseable { * <p>LIMITED-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices * support at least the following stream combinations for creating a reprocessable capture - * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for + * session in addition to those listed earlier for regular captures for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * * <table> @@ -671,74 +854,30 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - * <p>Clients can access the above mandatory stream combination tables via - * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p> - * - * @param inputConfig The configuration for the input {@link Surface} - * @param outputs The new set of Surfaces that should be made available as - * targets for captured image data. - * @param callback The callback to notify about the status of the new capture session. - * @param handler The handler on which the callback should be invoked, or {@code null} to use - * the current thread's {@link android.os.Looper looper}. - * - * @throws IllegalArgumentException if the input configuration is null or not supported, the set - * of output Surfaces do not meet the requirements, the - * callback is null, or the handler is null but the current - * thread has no looper. - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera device has been closed - * - * @see #createCaptureSession - * @see CameraCaptureSession - * @see StreamConfigurationMap#getInputFormats - * @see StreamConfigurationMap#getInputSizes - * @see StreamConfigurationMap#getValidOutputFormatsForInput - * @see StreamConfigurationMap#getOutputSizes - * @see android.media.ImageWriter - * @see android.media.ImageReader - */ - public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig, - @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback, - @Nullable Handler handler) - throws CameraAccessException; - - /** - * Create a new reprocessable camera capture session by providing the desired reprocessing - * input configuration and output {@link OutputConfiguration} - * to the camera device. - * - * @see #createReprocessableCaptureSession - * @see OutputConfiguration + * <h3>Constrained high-speed recording</h3> * - */ - public abstract void createReprocessableCaptureSessionByConfigurations( - @NonNull InputConfiguration inputConfig, - @NonNull List<OutputConfiguration> outputs, - @NonNull CameraCaptureSession.StateCallback callback, - @Nullable Handler handler) - throws CameraAccessException; - - /** - * <p>Create a new constrained high speed capture session.</p> - * - * <p>The application can use normal capture session (created via {@link #createCaptureSession}) + * <p>The application can use a + * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_REGULAR + * normal capture session} * for high speed capture if the desired high speed FPS ranges are advertised by * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}, in which case all API * semantics associated with normal capture sessions applies.</p> * - * <p>The method creates a specialized capture session that is only targeted at high speed - * video recording (>=120fps) use case if the camera device supports high speed video - * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains - * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}). - * Therefore, it has special characteristics compared with a normal capture session:</p> + * <p>A + * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_HIGH_SPEED + * high-speed capture session} + * can be use for high speed video recording (>=120fps) when the camera device supports high + * speed video capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} + * contains {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}). + * A constrained high-speed capture session has special limitations compared with a normal + * capture session:</p> * * <ul> * - * <li>In addition to the output target Surface requirements specified by the - * {@link #createCaptureSession} method, an active high speed capture session will support up - * to 2 output Surfaces, though the application might choose to configure just one Surface - * (e.g., preview only). All Surfaces must be either video encoder surfaces (acquired by + * <li>In addition to the output target Surface requirements specified above for regular + * captures, a high speed capture session will only support up to 2 output Surfaces, though + * the application might choose to configure just one Surface (e.g., preview only). All + * Surfaces must be either video encoder surfaces (acquired by * {@link android.media.MediaRecorder#getSurface} or * {@link android.media.MediaCodec#createInputSurface}) or preview surfaces (obtained from * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture} via @@ -774,116 +913,6 @@ public abstract class CameraDevice implements AutoCloseable { * * </ul> * - * @param outputs The new set of Surfaces that should be made available as - * targets for captured high speed image data. - * @param callback The callback to notify about the status of the new capture session. - * @param handler The handler on which the callback should be invoked, or {@code null} to use - * the current thread's {@link android.os.Looper looper}. - * - * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements, - * the callback is null, or the handler is null but the current - * thread has no looper, or the camera device doesn't support - * high speed video capability. - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera device has been closed - * - * @see #createCaptureSession - * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE - * @see StreamConfigurationMap#getHighSpeedVideoSizes - * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor - * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES - * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO - * @see CameraCaptureSession#captureBurst - * @see CameraCaptureSession#setRepeatingBurst - * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList - */ - public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs, - @NonNull CameraCaptureSession.StateCallback callback, - @Nullable Handler handler) - throws CameraAccessException; - - /** - * Standard camera operation mode. - * - * @see #createCustomCaptureSession - * @hide - */ - @SystemApi - @TestApi - public static final int SESSION_OPERATION_MODE_NORMAL = - 0; // ICameraDeviceUser.NORMAL_MODE; - - /** - * Constrained high-speed operation mode. - * - * @see #createCustomCaptureSession - * @hide - */ - @SystemApi - @TestApi - public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = - 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE; - - /** - * First vendor-specific operating mode - * - * @see #createCustomCaptureSession - * @hide - */ - @SystemApi - @TestApi - public static final int SESSION_OPERATION_MODE_VENDOR_START = - 0x8000; // ICameraDeviceUser.VENDOR_MODE_START; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value = - {SESSION_OPERATION_MODE_NORMAL, - SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED, - SESSION_OPERATION_MODE_VENDOR_START}) - public @interface SessionOperatingMode {}; - - /** - * Create a new camera capture session with a custom operating mode. - * - * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session - * is desired, or {@code null} otherwise. - * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be - * made available as targets for captured image data. - * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom - * vendor value or one of the SESSION_OPERATION_MODE_* values. - * @param callback The callback to notify about the status of the new capture session. - * @param handler The handler on which the callback should be invoked, or {@code null} to use - * the current thread's {@link android.os.Looper looper}. - * - * @throws IllegalArgumentException if the input configuration is null or not supported, the set - * of output Surfaces do not meet the requirements, the - * callback is null, or the handler is null but the current - * thread has no looper. - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera device has been closed - * - * @see #createCaptureSession - * @see #createReprocessableCaptureSession - * @see CameraCaptureSession - * @see OutputConfiguration - * @hide - */ - @SystemApi - @TestApi - public abstract void createCustomCaptureSession( - InputConfiguration inputConfig, - @NonNull List<OutputConfiguration> outputs, - @SessionOperatingMode int operatingMode, - @NonNull CameraCaptureSession.StateCallback callback, - @Nullable Handler handler) - throws CameraAccessException; - - /** - * <p>Create a new {@link CameraCaptureSession} using a {@link SessionConfiguration} helper - * object that aggregates all supported parameters.</p> * * @param config A session configuration (see {@link SessionConfiguration}). * @@ -997,7 +1026,7 @@ public abstract class CameraDevice implements AutoCloseable { * * @see CaptureRequest.Builder * @see TotalCaptureResult - * @see CameraDevice#createReprocessableCaptureSession + * @see CameraDevice#createCaptureSession(android.hardware.camera2.params.SessionConfiguration) * @see android.media.ImageWriter */ @NonNull @@ -1028,7 +1057,8 @@ public abstract class CameraDevice implements AutoCloseable { * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result * confirms whether or not the passed session configuration can be successfully used to * create a camera capture session using - * {@link CameraDevice#createCaptureSession(SessionConfiguration)}. + * {@link CameraDevice#createCaptureSession( + * android.hardware.camera2.params.SessionConfiguration)}. * </p> * * <p>The method can be called at any point before, during and after active capture session. diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 8e0a46d52dd6..ec13a36ca1d2 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1121,12 +1121,16 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in nanoseconds and monotonic, - * but can not be compared to timestamps from other subsystems - * (e.g. accelerometer, gyro etc.), or other instances of the same or different - * camera devices in the same system. Timestamps between streams and results for - * a single camera instance are comparable, and the timestamps for all buffers - * and the result metadata generated by a single capture are identical.</p> + * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in nanoseconds and monotonic, but can + * not be compared to timestamps from other subsystems (e.g. accelerometer, gyro etc.), + * or other instances of the same or different camera devices in the same system with + * accuracy. However, the timestamps are roughly in the same timebase as + * {@link android.os.SystemClock#uptimeMillis }. The accuracy is sufficient for tasks + * like A/V synchronization for video recording, at least, and the timestamps can be + * directly used together with timestamps from the audio subsystem for that task.</p> + * <p>Timestamps between streams and results for a single camera instance are comparable, + * and the timestamps for all buffers and the result metadata generated by a single + * capture are identical.</p> * * @see CaptureResult#SENSOR_TIMESTAMP * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE @@ -1137,6 +1141,14 @@ public abstract class CameraMetadata<TKey> { * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in the same timebase as * {@link android.os.SystemClock#elapsedRealtimeNanos }, * and they can be compared to other timestamps using that base.</p> + * <p>When buffers from a REALTIME device are passed directly to a video encoder from the + * camera, automatic compensation is done to account for differing timebases of the + * audio and camera subsystems. If the application is receiving buffers and then later + * sending them to a video encoder or other application where they are compared with + * audio subsystem timestamps or similar, this compensation is not present. In those + * cases, applications need to adjust the timestamps themselves. Since {@link android.os.SystemClock#elapsedRealtimeNanos } and {@link android.os.SystemClock#uptimeMillis } only diverge while the device is asleep, an + * offset between the two sources can be measured once per active session and applied + * to timestamps for sufficient accuracy for A/V sync.</p> * * @see CaptureResult#SENSOR_TIMESTAMP * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 555ff9aff184..47a897cb2c55 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -17,10 +17,11 @@ package android.hardware.camera2.params; +import static com.android.internal.util.Preconditions.*; + import android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.IntDef; +import android.annotation.NonNull; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; @@ -32,14 +33,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.ArrayList; import java.util.concurrent.Executor; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import static com.android.internal.util.Preconditions.*; /** * A helper class that aggregates all supported arguments for capture session initialization. @@ -61,6 +60,12 @@ public final class SessionConfiguration implements Parcelable { * A high speed session type that can only contain instances of {@link OutputConfiguration}. * The outputs can run using high speed FPS ranges. Calls to {@link #setInputConfiguration} * are not supported. + * <p> + * When using this type, the CameraCaptureSession returned by + * {@link android.hardware.camera2.CameraCaptureSession.StateCallback} can be cast to a + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession} to access the extra + * methods for constrained high speed recording. + * </p> * * @see CameraDevice#createConstrainedHighSpeedCaptureSession */ diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 60e466e5f278..a5e0f04eb034 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -303,16 +303,20 @@ public class SoundTrigger { @NonNull public final UUID vendorUuid; + /** vendor specific version number of the model */ + public final int version; + /** Opaque data. For use by vendor implementation and enrollment application */ @UnsupportedAppUsage @NonNull public final byte[] data; public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type, - @Nullable byte[] data) { + @Nullable byte[] data, int version) { this.uuid = requireNonNull(uuid); this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0); this.type = type; + this.version = version; this.data = data != null ? data : new byte[0]; } @@ -320,6 +324,7 @@ public class SoundTrigger { public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + version; result = prime * result + Arrays.hashCode(data); result = prime * result + type; result = prime * result + ((uuid == null) ? 0 : uuid.hashCode()); @@ -350,6 +355,8 @@ public class SoundTrigger { return false; if (!Arrays.equals(data, other.data)) return false; + if (version != other.version) + return false; return true; } } @@ -499,14 +506,19 @@ public class SoundTrigger { @NonNull public final Keyphrase[] keyphrases; // keyword phrases in model - @UnsupportedAppUsage public KeyphraseSoundModel( @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, - @Nullable Keyphrase[] keyphrases) { - super(uuid, vendorUuid, TYPE_KEYPHRASE, data); + @Nullable Keyphrase[] keyphrases, int version) { + super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version); this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0]; } + @UnsupportedAppUsage + public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, + @Nullable byte[] data, @Nullable Keyphrase[] keyphrases) { + this(uuid, vendorUuid, data, keyphrases, -1); + } + public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR = new Parcelable.Creator<KeyphraseSoundModel>() { public KeyphraseSoundModel createFromParcel(Parcel in) { @@ -525,9 +537,10 @@ public class SoundTrigger { if (length >= 0) { vendorUuid = UUID.fromString(in.readString()); } + int version = in.readInt(); byte[] data = in.readBlob(); Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR); - return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases); + return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version); } @Override @@ -546,13 +559,16 @@ public class SoundTrigger { } dest.writeBlob(data); dest.writeTypedArray(keyphrases, flags); + dest.writeInt(version); } @Override public String toString() { return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases) + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid - + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]"; + + ", type=" + type + + ", data=" + (data == null ? 0 : data.length) + + ", version=" + version + "]"; } @Override @@ -598,10 +614,15 @@ public class SoundTrigger { } }; + public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, + @Nullable byte[] data, int version) { + super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version); + } + @UnsupportedAppUsage public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data) { - super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data); + this(uuid, vendorUuid, data, -1); } @Override @@ -617,7 +638,8 @@ public class SoundTrigger { vendorUuid = UUID.fromString(in.readString()); } byte[] data = in.readBlob(); - return new GenericSoundModel(uuid, vendorUuid, data); + int version = in.readInt(); + return new GenericSoundModel(uuid, vendorUuid, data, version); } @Override @@ -630,12 +652,15 @@ public class SoundTrigger { dest.writeString(vendorUuid.toString()); } dest.writeBlob(data); + dest.writeInt(version); } @Override public String toString() { return "GenericSoundModel [uuid=" + uuid + ", vendorUuid=" + vendorUuid - + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]"; + + ", type=" + type + + ", data=" + (data == null ? 0 : data.length) + + ", version=" + version + "]"; } } diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java index a66fcae7d4a2..fb35b4bde303 100644 --- a/core/java/android/net/CaptivePortal.java +++ b/core/java/android/net/CaptivePortal.java @@ -60,6 +60,18 @@ public class CaptivePortal implements Parcelable { @SystemApi @TestApi public static final int APP_RETURN_WANTED_AS_IS = 2; + /** Event offset of request codes from captive portal application. */ + private static final int APP_REQUEST_BASE = 100; + /** + * Request code from the captive portal application, indicating that the network condition may + * have changed and the network should be re-validated. + * @see ICaptivePortal#appRequest(int) + * @see android.net.INetworkMonitor#forceReevaluation(int) + * @hide + */ + @SystemApi + @TestApi + public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0; private final IBinder mBinder; @@ -136,6 +148,19 @@ public class CaptivePortal implements Parcelable { } /** + * Request that the system reevaluates the captive portal status. + * @hide + */ + @SystemApi + @TestApi + public void reevaluateNetwork() { + try { + ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED); + } catch (RemoteException e) { + } + } + + /** * Log a captive portal login event. * @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto. * @param packageName captive portal application package name. diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 03d4200bd90f..4bf3e905697d 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3121,8 +3121,6 @@ public class ConnectivityManager { @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull NetworkProvider provider) { if (provider.getProviderId() != NetworkProvider.ID_NONE) { - // TODO: Provide a better method for checking this by moving NetworkFactory.SerialNumber - // out of NetworkFactory. throw new IllegalStateException("NetworkProviders can only be registered once"); } @@ -3175,9 +3173,8 @@ public class ConnectivityManager { */ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkMisc misc) { - return registerNetworkAgent(messenger, ni, lp, nc, score, misc, - NetworkFactory.SerialNumber.NONE); + NetworkCapabilities nc, int score, NetworkAgentConfig config) { + return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE); } /** @@ -3187,10 +3184,9 @@ public class ConnectivityManager { */ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkMisc misc, int factorySerialNumber) { + NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) { try { - return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc, - factorySerialNumber); + return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/net/ICaptivePortal.aidl b/core/java/android/net/ICaptivePortal.aidl index 707b4f699873..fe21905c7002 100644 --- a/core/java/android/net/ICaptivePortal.aidl +++ b/core/java/android/net/ICaptivePortal.aidl @@ -21,6 +21,7 @@ package android.net; * @hide */ oneway interface ICaptivePortal { + void appRequest(int request); void appResponse(int response); void logEvent(int eventId, String packageName); } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e6a0379ff629..7691beba3208 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -20,9 +20,9 @@ import android.app.PendingIntent; import android.net.ConnectionInfo; import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; -import android.net.NetworkMisc; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; @@ -153,7 +153,8 @@ interface IConnectivityManager void declareNetworkRequestUnfulfillable(in NetworkRequest request); int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, - in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber); + in NetworkCapabilities nc, int score, in NetworkAgentConfig config, + in int factorySerialNumber); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, in Messenger messenger, int timeoutSec, in IBinder binder, int legacy); diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 9994f9f82b07..5fa515a14343 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -23,6 +23,8 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; +import android.net.netstats.provider.INetworkStatsProvider; +import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.IBinder; import android.os.Messenger; import com.android.internal.net.VpnInfo; @@ -89,4 +91,7 @@ interface INetworkStatsService { /** Get the total network stats information since boot */ long getTotalStats(int type); + /** Registers a network stats provider */ + INetworkStatsProviderCallback registerNetworkStatsProvider(String tag, + in INetworkStatsProvider provider); } diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 60bd5730f8cf..fc72eecd4145 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -43,11 +43,12 @@ import java.util.concurrent.atomic.AtomicBoolean; * * @hide */ -public abstract class NetworkAgent extends Handler { +public abstract class NetworkAgent { // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown // an exception. public final int netId; + private final Handler mHandler; private volatile AsyncChannel mAsyncChannel; private final String LOG_TAG; private static final boolean DBG = true; @@ -58,7 +59,7 @@ public abstract class NetworkAgent extends Handler { private static final long BW_REFRESH_MIN_WIN_MS = 500; private boolean mPollLceScheduled = false; private AtomicBoolean mPollLcePending = new AtomicBoolean(false); - public final int mFactorySerialNumber; + public final int mProviderId; private static final int BASE = Protocol.BASE_NETWORK_AGENT; @@ -219,25 +220,25 @@ public abstract class NetworkAgent extends Handler { // the entire tree. public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score) { - this(looper, context, logTag, ni, nc, lp, score, null, NetworkFactory.SerialNumber.NONE); + this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE); } public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, - NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) { - this(looper, context, logTag, ni, nc, lp, score, misc, NetworkFactory.SerialNumber.NONE); + NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) { + this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE); } public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, - NetworkCapabilities nc, LinkProperties lp, int score, int factorySerialNumber) { - this(looper, context, logTag, ni, nc, lp, score, null, factorySerialNumber); + NetworkCapabilities nc, LinkProperties lp, int score, int providerId) { + this(looper, context, logTag, ni, nc, lp, score, null, providerId); } public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, - NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc, - int factorySerialNumber) { - super(looper); + NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config, + int providerId) { + mHandler = new NetworkAgentHandler(looper); LOG_TAG = logTag; mContext = context; - mFactorySerialNumber = factorySerialNumber; + mProviderId = providerId; if (ni == null || nc == null || lp == null) { throw new IllegalArgumentException(); } @@ -245,117 +246,124 @@ public abstract class NetworkAgent extends Handler { if (VDBG) log("Registering NetworkAgent"); ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( Context.CONNECTIVITY_SERVICE); - netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni), - new LinkProperties(lp), new NetworkCapabilities(nc), score, misc, - factorySerialNumber); + netId = cm.registerNetworkAgent(new Messenger(mHandler), new NetworkInfo(ni), + new LinkProperties(lp), new NetworkCapabilities(nc), score, config, + providerId); } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { - if (mAsyncChannel != null) { - log("Received new connection while already connected!"); - } else { - if (VDBG) log("NetworkAgent fully connected"); - AsyncChannel ac = new AsyncChannel(); - ac.connected(null, this, msg.replyTo); - ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, - AsyncChannel.STATUS_SUCCESSFUL); - synchronized (mPreConnectedQueue) { - mAsyncChannel = ac; - for (Message m : mPreConnectedQueue) { - ac.sendMessage(m); + private class NetworkAgentHandler extends Handler { + NetworkAgentHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { + if (mAsyncChannel != null) { + log("Received new connection while already connected!"); + } else { + if (VDBG) log("NetworkAgent fully connected"); + AsyncChannel ac = new AsyncChannel(); + ac.connected(null, this, msg.replyTo); + ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, + AsyncChannel.STATUS_SUCCESSFUL); + synchronized (mPreConnectedQueue) { + mAsyncChannel = ac; + for (Message m : mPreConnectedQueue) { + ac.sendMessage(m); + } + mPreConnectedQueue.clear(); } - mPreConnectedQueue.clear(); } + break; } - break; - } - case AsyncChannel.CMD_CHANNEL_DISCONNECT: { - if (VDBG) log("CMD_CHANNEL_DISCONNECT"); - if (mAsyncChannel != null) mAsyncChannel.disconnect(); - break; - } - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { - if (DBG) log("NetworkAgent channel lost"); - // let the client know CS is done with us. - unwanted(); - synchronized (mPreConnectedQueue) { - mAsyncChannel = null; + case AsyncChannel.CMD_CHANNEL_DISCONNECT: { + if (VDBG) log("CMD_CHANNEL_DISCONNECT"); + if (mAsyncChannel != null) mAsyncChannel.disconnect(); + break; } - break; - } - case CMD_SUSPECT_BAD: { - log("Unhandled Message " + msg); - break; - } - case CMD_REQUEST_BANDWIDTH_UPDATE: { - long currentTimeMs = System.currentTimeMillis(); - if (VDBG) { - log("CMD_REQUEST_BANDWIDTH_UPDATE request received."); + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + if (DBG) log("NetworkAgent channel lost"); + // let the client know CS is done with us. + unwanted(); + synchronized (mPreConnectedQueue) { + mAsyncChannel = null; + } + break; } - if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) { - mPollLceScheduled = false; - if (mPollLcePending.getAndSet(true) == false) { - pollLceData(); + case CMD_SUSPECT_BAD: { + log("Unhandled Message " + msg); + break; + } + case CMD_REQUEST_BANDWIDTH_UPDATE: { + long currentTimeMs = System.currentTimeMillis(); + if (VDBG) { + log("CMD_REQUEST_BANDWIDTH_UPDATE request received."); } - } else { - // deliver the request at a later time rather than discard it completely. - if (!mPollLceScheduled) { - long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS - - currentTimeMs + 1; - mPollLceScheduled = sendEmptyMessageDelayed( - CMD_REQUEST_BANDWIDTH_UPDATE, waitTime); + if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) { + mPollLceScheduled = false; + if (!mPollLcePending.getAndSet(true)) { + pollLceData(); + } + } else { + // deliver the request at a later time rather than discard it completely. + if (!mPollLceScheduled) { + long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS + - currentTimeMs + 1; + mPollLceScheduled = sendEmptyMessageDelayed( + CMD_REQUEST_BANDWIDTH_UPDATE, waitTime); + } } + break; } - break; - } - case CMD_REPORT_NETWORK_STATUS: { - String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY); - if (VDBG) { - log("CMD_REPORT_NETWORK_STATUS(" + - (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl); + case CMD_REPORT_NETWORK_STATUS: { + String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY); + if (VDBG) { + log("CMD_REPORT_NETWORK_STATUS(" + + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + + redirectUrl); + } + networkStatus(msg.arg1, redirectUrl); + break; + } + case CMD_SAVE_ACCEPT_UNVALIDATED: { + saveAcceptUnvalidated(msg.arg1 != 0); + break; + } + case CMD_START_SOCKET_KEEPALIVE: { + startSocketKeepalive(msg); + break; + } + case CMD_STOP_SOCKET_KEEPALIVE: { + stopSocketKeepalive(msg); + break; } - networkStatus(msg.arg1, redirectUrl); - break; - } - case CMD_SAVE_ACCEPT_UNVALIDATED: { - saveAcceptUnvalidated(msg.arg1 != 0); - break; - } - case CMD_START_SOCKET_KEEPALIVE: { - startSocketKeepalive(msg); - break; - } - case CMD_STOP_SOCKET_KEEPALIVE: { - stopSocketKeepalive(msg); - break; - } - case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: { - ArrayList<Integer> thresholds = - ((Bundle) msg.obj).getIntegerArrayList("thresholds"); - // TODO: Change signal strength thresholds API to use an ArrayList<Integer> - // rather than convert to int[]. - int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0]; - for (int i = 0; i < intThresholds.length; i++) { - intThresholds[i] = thresholds.get(i); + case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: { + ArrayList<Integer> thresholds = + ((Bundle) msg.obj).getIntegerArrayList("thresholds"); + // TODO: Change signal strength thresholds API to use an ArrayList<Integer> + // rather than convert to int[]. + int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0]; + for (int i = 0; i < intThresholds.length; i++) { + intThresholds[i] = thresholds.get(i); + } + setSignalStrengthThresholds(intThresholds); + break; + } + case CMD_PREVENT_AUTOMATIC_RECONNECT: { + preventAutomaticReconnect(); + break; + } + case CMD_ADD_KEEPALIVE_PACKET_FILTER: { + addKeepalivePacketFilter(msg); + break; + } + case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: { + removeKeepalivePacketFilter(msg); + break; } - setSignalStrengthThresholds(intThresholds); - break; - } - case CMD_PREVENT_AUTOMATIC_RECONNECT: { - preventAutomaticReconnect(); - break; - } - case CMD_ADD_KEEPALIVE_PACKET_FILTER: { - addKeepalivePacketFilter(msg); - break; - } - case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: { - removeKeepalivePacketFilter(msg); - break; } } } diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/NetworkAgentConfig.aidl index c65583fb530a..cb70bdd31260 100644 --- a/core/java/android/net/NetworkMisc.aidl +++ b/core/java/android/net/NetworkAgentConfig.aidl @@ -16,4 +16,4 @@ package android.net; -parcelable NetworkMisc; +parcelable NetworkAgentConfig; diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkAgentConfig.java index 4ad52d5aa1bc..3a383a44cdc3 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkAgentConfig.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -26,7 +28,7 @@ import android.os.Parcelable; * * @hide */ -public class NetworkMisc implements Parcelable { +public class NetworkAgentConfig implements Parcelable { /** * If the {@link Network} is a VPN, whether apps are allowed to bypass the @@ -83,17 +85,17 @@ public class NetworkMisc implements Parcelable { */ public boolean hasShownBroken; - public NetworkMisc() { + public NetworkAgentConfig() { } - public NetworkMisc(NetworkMisc nm) { - if (nm != null) { - allowBypass = nm.allowBypass; - explicitlySelected = nm.explicitlySelected; - acceptUnvalidated = nm.acceptUnvalidated; - subscriberId = nm.subscriberId; - provisioningNotificationDisabled = nm.provisioningNotificationDisabled; - skip464xlat = nm.skip464xlat; + public NetworkAgentConfig(@Nullable NetworkAgentConfig nac) { + if (nac != null) { + allowBypass = nac.allowBypass; + explicitlySelected = nac.explicitlySelected; + acceptUnvalidated = nac.acceptUnvalidated; + subscriberId = nac.subscriberId; + provisioningNotificationDisabled = nac.provisioningNotificationDisabled; + skip464xlat = nac.skip464xlat; } } @@ -112,22 +114,23 @@ public class NetworkMisc implements Parcelable { out.writeInt(skip464xlat ? 1 : 0); } - public static final @android.annotation.NonNull Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() { + public static final @NonNull Creator<NetworkAgentConfig> CREATOR = + new Creator<NetworkAgentConfig>() { @Override - public NetworkMisc createFromParcel(Parcel in) { - NetworkMisc networkMisc = new NetworkMisc(); - networkMisc.allowBypass = in.readInt() != 0; - networkMisc.explicitlySelected = in.readInt() != 0; - networkMisc.acceptUnvalidated = in.readInt() != 0; - networkMisc.subscriberId = in.readString(); - networkMisc.provisioningNotificationDisabled = in.readInt() != 0; - networkMisc.skip464xlat = in.readInt() != 0; - return networkMisc; + public NetworkAgentConfig createFromParcel(Parcel in) { + NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); + networkAgentConfig.allowBypass = in.readInt() != 0; + networkAgentConfig.explicitlySelected = in.readInt() != 0; + networkAgentConfig.acceptUnvalidated = in.readInt() != 0; + networkAgentConfig.subscriberId = in.readString(); + networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0; + networkAgentConfig.skip464xlat = in.readInt() != 0; + return networkAgentConfig; } @Override - public NetworkMisc[] newArray(int size) { - return new NetworkMisc[size]; + public NetworkAgentConfig[] newArray(int size) { + return new NetworkAgentConfig[size]; } }; } diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index f10ea20467a8..824ddb8dd260 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -91,7 +91,7 @@ public class NetworkFactory extends Handler { * with the same NetworkRequest but an updated score. * Also, network conditions may change for this bearer * allowing for a better score in the future. - * msg.arg2 = the serial number of the factory currently responsible for the + * msg.arg2 = the ID of the NetworkProvider currently responsible for the * NetworkAgent handling this request, or NetworkProvider.ID_NONE if none. */ public static final int CMD_REQUEST_NETWORK = BASE; @@ -156,8 +156,8 @@ public class NetworkFactory extends Handler { mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) { @Override public void onNetworkRequested(@NonNull NetworkRequest request, int score, - int servingFactoryProviderId) { - handleAddRequest((NetworkRequest) request, score, servingFactoryProviderId); + int servingProviderId) { + handleAddRequest((NetworkRequest) request, score, servingProviderId); } @Override @@ -228,8 +228,6 @@ public class NetworkFactory extends Handler { * * @param request the request to handle. * @param score the score of the NetworkAgent currently satisfying this request. - * @param servingProviderId the serial number of the NetworkFactory that - * created the NetworkAgent currently satisfying this request. */ // TODO : remove this method. It is a stopgap measure to help sheperding a number // of dependent changes that would conflict throughout the automerger graph. Having this @@ -237,7 +235,7 @@ public class NetworkFactory extends Handler { // the entire tree. @VisibleForTesting protected void handleAddRequest(NetworkRequest request, int score) { - handleAddRequest(request, score, SerialNumber.NONE); + handleAddRequest(request, score, NetworkProvider.ID_NONE); } /** @@ -246,24 +244,23 @@ public class NetworkFactory extends Handler { * * @param request the request to handle. * @param score the score of the NetworkAgent currently satisfying this request. - * @param servingProviderId the serial number of the NetworkFactory that - * created the NetworkAgent currently satisfying this request. + * @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent + * currently satisfying this request. */ @VisibleForTesting - protected void handleAddRequest(NetworkRequest request, int score, - int servingProviderId) { + protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) { NetworkRequestInfo n = mNetworkRequests.get(request.requestId); if (n == null) { if (DBG) { log("got request " + request + " with score " + score - + " and serial " + servingProviderId); + + " and providerId " + servingProviderId); } n = new NetworkRequestInfo(request, score, servingProviderId); mNetworkRequests.put(n.request.requestId, n); } else { if (VDBG) { log("new score " + score + " for exisiting request " + request - + " with serial " + servingProviderId); + + " and providerId " + servingProviderId); } n.score = score; n.providerId = servingProviderId; diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index b0c2546e0300..ac70b523bcc1 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -631,10 +631,12 @@ public final class BinderProxy implements IBinder { } } - private static void sendDeathNotice(DeathRecipient recipient) { - if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient); + private static void sendDeathNotice(DeathRecipient recipient, IBinder binderProxy) { + if (false) { + Log.v("JavaBinder", "sendDeathNotice to " + recipient + " for " + binderProxy); + } try { - recipient.binderDied(); + recipient.binderDied(binderProxy); } catch (RuntimeException exc) { Log.w("BinderNative", "Uncaught exception from death notification", exc); diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index f336fdab5941..f5fe9c3334bd 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -285,6 +285,13 @@ public interface IBinder { */ public interface DeathRecipient { public void binderDied(); + + /** + * @hide + */ + default void binderDied(IBinder who) { + binderDied(); + } } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a31c3d1749c6..548b1230d6e2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -510,28 +510,31 @@ public final class Settings { "android.settings.WIFI_IP_SETTINGS"; /** - * Activity Action: Show setting page to process an Easy Connect (Wi-Fi DPP) QR code and start + * Activity Action: Show setting page to process a Wi-Fi Easy Connect (aka DPP) URI and start * configuration. This intent should be used when you want to use this device to take on the - * configurator role for an IoT/other device. When provided with a valid DPP URI string Settings - * will open a wifi selection screen for the user to indicate which network they would like to - * configure the device specified in the DPP URI string for and carry them through the rest of - * the flow for provisioning the device. + * configurator role for an IoT/other device. When provided with a valid DPP URI + * string, Settings will open a Wi-Fi selection screen for the user to indicate which network + * they would like to configure the device specified in the DPP URI string and + * carry them through the rest of the flow for provisioning the device. * <p> - * In some cases, a matching Activity may not exist, so ensure you safeguard against this by - * checking WifiManager.isEasyConnectSupported(); + * In some cases, a matching Activity may not exist, so ensure to safeguard against this by + * checking {@link WifiManager#isEasyConnectSupported()}. * <p> * Input: The Intent's data URI specifies bootstrapping information for authenticating and - * provisioning the peer, with the "DPP" scheme. - * <p> - * Output: After {@code startActivityForResult}, the callback {@code onActivityResult} will have - * resultCode {@link android.app.Activity#RESULT_OK} if Wi-Fi Easy Connect configuration succeeded - * and the user tapped 'Done' button, or {@link android.app.Activity#RESULT_CANCELED} if operation - * failed and user tapped 'Cancel'. In case the operation has failed, a status code from {@link - * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode} will be returned as - * Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2 Enrollees report additional - * details about the error they encountered, which will be provided in the {@link - * #EXTRA_EASY_CONNECT_ATTEMPTED_SSID}, {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link - * #EXTRA_EASY_CONNECT_BAND_LIST}. + * provisioning the peer, and uses a "DPP" scheme. The URI should be attached to the intent + * using {@link Intent#setData(Uri)}. The calling app can obtain a DPP URI in any + * way, e.g. by scanning a QR code or other out-of-band methods. + * <p> + * Output: After calling {@link android.app.Activity#startActivityForResult}, the callback + * {@code onActivityResult} will have resultCode {@link android.app.Activity#RESULT_OK} if + * the Wi-Fi Easy Connect configuration succeeded and the user tapped the 'Done' button, or + * {@link android.app.Activity#RESULT_CANCELED} if the operation failed and user tapped the + * 'Cancel' button. In case the operation has failed, a status code from + * {@link android.net.wifi.EasyConnectStatusCallback} {@code EASY_CONNECT_EVENT_FAILURE_*} will + * be returned as an Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2 + * Enrollees report additional details about the error they encountered, which will be + * provided in the {@link #EXTRA_EASY_CONNECT_ATTEMPTED_SSID}, + * {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link #EXTRA_EASY_CONNECT_BAND_LIST}. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = @@ -540,12 +543,15 @@ public final class Settings { /** * Activity Extra: The Easy Connect operation error code * <p> - * An extra returned on the result intent received when using the {@link - * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This - * extra contains the error code of the operation - one of - * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode}. - * If there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, + * An extra returned on the result intent received when using the + * {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. + * This extra contains the integer error code of the operation - one of + * {@link android.net.wifi.EasyConnectStatusCallback} {@code EASY_CONNECT_EVENT_FAILURE_*}. If + * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, * then this extra is not attached to the result intent. + * <p> + * Use the {@link Intent#hasExtra(String)} to determine whether the extra is attached and + * {@link Intent#getIntExtra(String, int)} to obtain the error code data. */ public static final String EXTRA_EASY_CONNECT_ERROR_CODE = "android.provider.extra.EASY_CONNECT_ERROR_CODE"; @@ -557,11 +563,13 @@ public final class Settings { * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This * extra contains the SSID of the Access Point that the remote Enrollee tried to connect to. * This value is populated only by remote R2 devices, and only for the following error codes: - * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK} - * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}. + * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK} + * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}. * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then * this extra is not attached to the result intent. + * <p> + * Use the {@link Intent#getStringExtra(String)} to obtain the SSID. */ public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID = "android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID"; @@ -571,13 +579,15 @@ public final class Settings { * <p> * An extra returned on the result intent received when using the {@link * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This - * extra contains the list channels the Enrollee used to scan for a network. This value is + * extra contains the channel list that the Enrollee scanned for a network. This value is * populated only by remote R2 devices, and only for the following error code: {@link - * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}. + * android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}. * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then * this extra is not attached to the result intent. The list is JSON formatted, as an array * (Wi-Fi global operating classes) of arrays (Wi-Fi channels). + * <p> + * Use the {@link Intent#getStringExtra(String)} to obtain the list. */ public static final String EXTRA_EASY_CONNECT_CHANNEL_LIST = "android.provider.extra.EASY_CONNECT_CHANNEL_LIST"; @@ -588,14 +598,16 @@ public final class Settings { * An extra returned on the result intent received when using the {@link * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This * extra contains the bands the Enrollee supports, expressed as the Global Operating Class, - * see Table E-4 in IEEE Std 802.11-2016 -Global operating classes. This value is populated only + * see Table E-4 in IEEE Std 802.11-2016 Global operating classes. This value is populated only * by remote R2 devices, and only for the following error codes: {@link - * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK} - * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION} - * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}. + * android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK} + * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION} + * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}. * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then * this extra is not attached to the result intent. + * <p> + * Use the {@link Intent#getIntArrayExtra(String)} to obtain the list. */ public static final String EXTRA_EASY_CONNECT_BAND_LIST = "android.provider.extra.EASY_CONNECT_BAND_LIST"; diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 2e7ac3f505fa..f3690648f35b 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4142,7 +4142,6 @@ public final class Telephony { * <li>{@link #ENABLE_CMAS_PRESIDENTIAL_PREF}</li> * <li>{@link #ENABLE_ALERT_VIBRATION_PREF}</li> * <li>{@link #ENABLE_EMERGENCY_PERF}</li> - * <li>{@link #ENABLE_FULL_VOLUME_PREF}</li> * <li>{@link #ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF}</li> * </ul> * @hide @@ -4205,10 +4204,6 @@ public final class Telephony { public static final @NonNull String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts"; - /** Preference to enable volume for alerts */ - public static final @NonNull String ENABLE_FULL_VOLUME_PREF = - "use_full_volume"; - /** Preference to enable receive alerts in second language */ public static final @NonNull String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF = "receive_cmas_in_second_language"; diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java index d646e23d230a..00060ab8ef4a 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/core/java/android/se/omapi/SEService.java @@ -22,14 +22,11 @@ package android.se.omapi; -import android.app.ActivityThread; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -143,10 +140,6 @@ public final class SEService { throw new NullPointerException("Arguments must not be null"); } - if (!hasOMAPIReaders()) { - throw new UnsupportedOperationException("Device does not support any OMAPI reader"); - } - mContext = context; mSEListener.mListener = listener; mSEListener.mExecutor = executor; @@ -277,23 +270,4 @@ public final class SEService { throw new IllegalStateException(e.getMessage()); } } - - /** - * Helper to check if this device support any OMAPI readers - */ - private static boolean hasOMAPIReaders() { - IPackageManager pm = ActivityThread.getPackageManager(); - if (pm == null) { - Log.e(TAG, "Cannot get package manager, assuming OMAPI readers supported"); - return true; - } - try { - return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC, 0) - || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE, 0) - || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD, 0); - } catch (RemoteException e) { - Log.e(TAG, "Package manager query failed, assuming OMAPI readers supported", e); - return true; - } - } } diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index d827b30f27b6..262d9896df87 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -21,6 +21,7 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; @@ -238,6 +239,7 @@ public final class Dataset implements Parcelable { public Builder(@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) { Preconditions.checkNotNull(presentation, "presentation must be non-null"); + Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null"); mPresentation = presentation; mInlinePresentation = inlinePresentation; } @@ -248,7 +250,8 @@ public final class Dataset implements Parcelable { * @param presentation The presentation used to visualize this dataset. */ public Builder(@NonNull RemoteViews presentation) { - this(presentation, null); + Preconditions.checkNotNull(presentation, "presentation must be non-null"); + mPresentation = presentation; } /** @@ -262,7 +265,9 @@ public final class Dataset implements Parcelable { * @hide */ @SystemApi + @TestApi public Builder(@NonNull InlinePresentation inlinePresentation) { + Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null"); mInlinePresentation = inlinePresentation; } @@ -576,6 +581,7 @@ public final class Dataset implements Parcelable { * @hide */ @SystemApi + @TestApi public @NonNull Builder setInlinePresentation(@NonNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull InlinePresentation inlinePresentation) { @@ -672,11 +678,13 @@ public final class Dataset implements Parcelable { // using specially crafted parcels. final RemoteViews presentation = parcel.readParcelable(null); final InlinePresentation inlinePresentation = parcel.readParcelable(null); - final Builder builder = presentation == null - ? new Builder(inlinePresentation) - : inlinePresentation == null + final Builder builder = presentation != null + ? inlinePresentation == null ? new Builder(presentation) - : new Builder(presentation, inlinePresentation); + : new Builder(presentation, inlinePresentation) + : inlinePresentation == null + ? new Builder() + : new Builder(inlinePresentation); final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); final ArrayList<AutofillValue> values = diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index fa994ba76fd7..0892c94d5bec 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -16,6 +16,8 @@ package android.util; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.Context; @@ -25,172 +27,270 @@ import android.net.Network; import android.net.NetworkInfo; import android.net.SntpClient; import android.os.SystemClock; -import android.os.TimestampedValue; import android.provider.Settings; import android.text.TextUtils; +import com.android.internal.annotations.GuardedBy; + +import java.util.Objects; +import java.util.function.Supplier; + /** - * {@link TrustedTime} that connects with a remote NTP server as its trusted - * time source. + * A singleton that connects with a remote NTP server as its trusted time source. This class + * is thread-safe. The {@link #forceRefresh()} method is synchronous, i.e. it may occupy the + * current thread while performing an NTP request. All other threads calling {@link #forceRefresh()} + * will block during that request. * * @hide */ public class NtpTrustedTime implements TrustedTime { + + /** + * The result of a successful NTP query. + * + * @hide + */ + public static class TimeResult { + private final long mTimeMillis; + private final long mElapsedRealtimeMillis; + private final long mCertaintyMillis; + + public TimeResult(long timeMillis, long elapsedRealtimeMillis, long certaintyMillis) { + mTimeMillis = timeMillis; + mElapsedRealtimeMillis = elapsedRealtimeMillis; + mCertaintyMillis = certaintyMillis; + } + + public long getTimeMillis() { + return mTimeMillis; + } + + public long getElapsedRealtimeMillis() { + return mElapsedRealtimeMillis; + } + + public long getCertaintyMillis() { + return mCertaintyMillis; + } + + /** Calculates and returns the current time accounting for the age of this result. */ + public long currentTimeMillis() { + return mTimeMillis + getAgeMillis(); + } + + /** Calculates and returns the age of this result. */ + public long getAgeMillis() { + return SystemClock.elapsedRealtime() - mElapsedRealtimeMillis; + } + + @Override + public String toString() { + return "TimeResult{" + + "mTimeMillis=" + mTimeMillis + + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis + + ", mCertaintyMillis=" + mCertaintyMillis + + '}'; + } + } + private static final String TAG = "NtpTrustedTime"; private static final boolean LOGD = false; private static NtpTrustedTime sSingleton; - private static Context sContext; - private final String mServer; - private final long mTimeout; + @NonNull + private final Context mContext; + + /** + * A supplier that returns the ConnectivityManager. The Supplier can return null if + * ConnectivityService isn't running yet. + */ + private final Supplier<ConnectivityManager> mConnectivityManagerSupplier = + new Supplier<ConnectivityManager>() { + private ConnectivityManager mConnectivityManager; - private ConnectivityManager mCM; + @Nullable + @Override + public synchronized ConnectivityManager get() { + // We can't do this at initialization time: ConnectivityService might not be running + // yet. + if (mConnectivityManager == null) { + mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); + } + return mConnectivityManager; + } + }; - private boolean mHasCache; - private long mCachedNtpTime; - private long mCachedNtpElapsedRealtime; - private long mCachedNtpCertainty; + // Declared volatile and accessed outside of synchronized blocks to avoid blocking reads during + // forceRefresh(). + private volatile TimeResult mTimeResult; - private NtpTrustedTime(String server, long timeout) { - if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server); - mServer = server; - mTimeout = timeout; + private NtpTrustedTime(Context context) { + mContext = Objects.requireNonNull(context); } @UnsupportedAppUsage public static synchronized NtpTrustedTime getInstance(Context context) { if (sSingleton == null) { - final Resources res = context.getResources(); - final ContentResolver resolver = context.getContentResolver(); - - final String defaultServer = res.getString( - com.android.internal.R.string.config_ntpServer); - final long defaultTimeout = res.getInteger( - com.android.internal.R.integer.config_ntpTimeout); - - final String secureServer = Settings.Global.getString( - resolver, Settings.Global.NTP_SERVER); - final long timeout = Settings.Global.getLong( - resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout); - - final String server = secureServer != null ? secureServer : defaultServer; - sSingleton = new NtpTrustedTime(server, timeout); - sContext = context; + Context appContext = context.getApplicationContext(); + sSingleton = new NtpTrustedTime(appContext); } - return sSingleton; } - @Override @UnsupportedAppUsage public boolean forceRefresh() { - // We can't do this at initialization time: ConnectivityService might not be running yet. synchronized (this) { - if (mCM == null) { - mCM = sContext.getSystemService(ConnectivityManager.class); + NtpConnectionInfo connectionInfo = getNtpConnectionInfo(); + if (connectionInfo == null) { + // missing server config, so no trusted time available + if (LOGD) Log.d(TAG, "forceRefresh: invalid server config"); + return false; } - } - final Network network = mCM == null ? null : mCM.getActiveNetwork(); - return forceRefresh(network); - } - - public boolean forceRefresh(Network network) { - if (TextUtils.isEmpty(mServer)) { - // missing server, so no trusted time available - return false; - } - - // We can't do this at initialization time: ConnectivityService might not be running yet. - synchronized (this) { - if (mCM == null) { - mCM = sContext.getSystemService(ConnectivityManager.class); + ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get(); + if (connectivityManager == null) { + if (LOGD) Log.d(TAG, "forceRefresh: no ConnectivityManager"); + return false; + } + final Network network = connectivityManager.getActiveNetwork(); + final NetworkInfo ni = connectivityManager.getNetworkInfo(network); + if (ni == null || !ni.isConnected()) { + if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); + return false; } - } - - final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network); - if (ni == null || !ni.isConnected()) { - if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); - return false; - } - - if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); - final SntpClient client = new SntpClient(); - if (client.requestTime(mServer, (int) mTimeout, network)) { - mHasCache = true; - mCachedNtpTime = client.getNtpTime(); - mCachedNtpElapsedRealtime = client.getNtpTimeReference(); - mCachedNtpCertainty = client.getRoundTripTime() / 2; - return true; - } else { - return false; + if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); + final SntpClient client = new SntpClient(); + final String serverName = connectionInfo.getServer(); + final int timeoutMillis = connectionInfo.getTimeoutMillis(); + if (client.requestTime(serverName, timeoutMillis, network)) { + long ntpCertainty = client.getRoundTripTime() / 2; + mTimeResult = new TimeResult( + client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty); + return true; + } else { + return false; + } } } - @Override + /** + * Only kept for UnsupportedAppUsage. + * + * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically. + */ + @Deprecated @UnsupportedAppUsage public boolean hasCache() { - return mHasCache; + return mTimeResult != null; } + /** + * Only kept for UnsupportedAppUsage. + * + * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically. + */ + @Deprecated @Override public long getCacheAge() { - if (mHasCache) { - return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime; + TimeResult timeResult = mTimeResult; + if (timeResult != null) { + return SystemClock.elapsedRealtime() - timeResult.getElapsedRealtimeMillis(); } else { return Long.MAX_VALUE; } } - @Override - public long getCacheCertainty() { - if (mHasCache) { - return mCachedNtpCertainty; - } else { - return Long.MAX_VALUE; - } - } - - @Override + /** + * Only kept for UnsupportedAppUsage. + * + * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically. + */ + @Deprecated @UnsupportedAppUsage public long currentTimeMillis() { - if (!mHasCache) { + TimeResult timeResult = mTimeResult; + if (timeResult == null) { throw new IllegalStateException("Missing authoritative time source"); } if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit"); // current time is age after the last ntp cache; callers who - // want fresh values will hit makeAuthoritative() first. - return mCachedNtpTime + getCacheAge(); + // want fresh values will hit forceRefresh() first. + return timeResult.currentTimeMillis(); } + /** + * Only kept for UnsupportedAppUsage. + * + * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically. + */ + @Deprecated @UnsupportedAppUsage public long getCachedNtpTime() { if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit"); - return mCachedNtpTime; + TimeResult timeResult = mTimeResult; + return timeResult == null ? 0 : timeResult.getTimeMillis(); } + /** + * Only kept for UnsupportedAppUsage. + * + * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically. + */ + @Deprecated @UnsupportedAppUsage public long getCachedNtpTimeReference() { - return mCachedNtpElapsedRealtime; + TimeResult timeResult = mTimeResult; + return timeResult == null ? 0 : timeResult.getElapsedRealtimeMillis(); } /** - * Returns the combination of {@link #getCachedNtpTime()} and {@link - * #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when - * passing the time to another component that will adjust for elapsed time. - * - * @throws IllegalStateException if there is no cached value + * Returns an object containing the latest NTP information available. Can return {@code null} if + * no information is available. */ - public TimestampedValue<Long> getCachedNtpTimeSignal() { - if (!mHasCache) { - throw new IllegalStateException("Missing authoritative time source"); + @Nullable + public TimeResult getCachedTimeResult() { + return mTimeResult; + } + + private static class NtpConnectionInfo { + + @NonNull private final String mServer; + private final int mTimeoutMillis; + + NtpConnectionInfo(@NonNull String server, int timeoutMillis) { + mServer = Objects.requireNonNull(server); + mTimeoutMillis = timeoutMillis; } - if (LOGD) Log.d(TAG, "getCachedNtpTimeSignal() cache hit"); - return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime); + @NonNull + public String getServer() { + return mServer; + } + + int getTimeoutMillis() { + return mTimeoutMillis; + } } + @GuardedBy("this") + private NtpConnectionInfo getNtpConnectionInfo() { + final ContentResolver resolver = mContext.getContentResolver(); + + final Resources res = mContext.getResources(); + final String defaultServer = res.getString( + com.android.internal.R.string.config_ntpServer); + final int defaultTimeoutMillis = res.getInteger( + com.android.internal.R.integer.config_ntpTimeout); + + final String secureServer = Settings.Global.getString( + resolver, Settings.Global.NTP_SERVER); + final int timeoutMillis = Settings.Global.getInt( + resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis); + + final String server = secureServer != null ? secureServer : defaultServer; + return TextUtils.isEmpty(server) ? null : new NtpConnectionInfo(server, timeoutMillis); + } } diff --git a/core/java/android/util/TrustedTime.java b/core/java/android/util/TrustedTime.java index 1360f874507f..f41fe85fa8bb 100644 --- a/core/java/android/util/TrustedTime.java +++ b/core/java/android/util/TrustedTime.java @@ -20,42 +20,48 @@ import android.compat.annotation.UnsupportedAppUsage; /** * Interface that provides trusted time information, possibly coming from an NTP - * server. Implementations may cache answers until {@link #forceRefresh()}. + * server. * * @hide + * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime} */ public interface TrustedTime { /** * Force update with an external trusted time source, returning {@code true} * when successful. + * + * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime} */ + @Deprecated @UnsupportedAppUsage public boolean forceRefresh(); /** * Check if this instance has cached a response from a trusted time source. + * + * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime} */ + @Deprecated @UnsupportedAppUsage - public boolean hasCache(); + boolean hasCache(); /** * Return time since last trusted time source contact, or * {@link Long#MAX_VALUE} if never contacted. + * + * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime} */ + @Deprecated @UnsupportedAppUsage public long getCacheAge(); /** - * Return certainty of cached trusted time in milliseconds, or - * {@link Long#MAX_VALUE} if never contacted. Smaller values are more - * precise. - */ - public long getCacheCertainty(); - - /** * Return current time similar to {@link System#currentTimeMillis()}, * possibly using a cached authoritative time source. + * + * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime} */ + @Deprecated @UnsupportedAppUsage - public long currentTimeMillis(); + long currentTimeMillis(); } diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java index c45a3a4b8b14..2bb3f1fed927 100644 --- a/core/java/com/android/ims/internal/uce/common/CapInfo.java +++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java @@ -64,6 +64,20 @@ public class CapInfo implements Parcelable { private boolean mRcsIpVideoCallSupported = false; /** RCS IP Video call support . */ private boolean mRcsIpVideoOnlyCallSupported = false; + /** IP Geo location Push using SMS. */ + private boolean mGeoSmsSupported = false; + /** RCS call composer support. */ + private boolean mCallComposerSupported = false; + /** RCS post-call support. */ + private boolean mPostCallSupported = false; + /** Shared map support. */ + private boolean mSharedMapSupported = false; + /** Shared Sketch supported. */ + private boolean mSharedSketchSupported = false; + /** Chatbot communication support. */ + private boolean mChatbotSupported = false; + /** Chatbot role support. */ + private boolean mChatbotRoleSupported = false; /** List of supported extensions. */ private String[] mExts = new String[10]; /** Time used to compute when to query again. */ @@ -386,6 +400,104 @@ public class CapInfo implements Parcelable { this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported; } + /** + * Checks whether Geo Push via SMS is supported. + */ + public boolean isGeoSmsSupported() { + return mGeoSmsSupported; + } + + /** + * Sets Geolocation Push via SMS as supported or not supported. + */ + public void setGeoSmsSupported(boolean geoSmsSupported) { + this.mGeoSmsSupported = geoSmsSupported; + } + + /** + * Checks whether RCS call composer is supported. + */ + public boolean isCallComposerSupported() { + return mCallComposerSupported; + } + + /** + * Sets call composer as supported or not supported. + */ + public void setCallComposerSupported(boolean callComposerSupported) { + this.mCallComposerSupported = callComposerSupported; + } + + /** + * Checks whether post call is supported. + */ + public boolean isPostCallSupported(){ + return mPostCallSupported; + } + + /** + * Sets post call as supported or not supported. + */ + public void setPostCallSupported(boolean postCallSupported) { + this.mPostCallSupported = postCallSupported; + } + + /** + * Checks whether shared map is supported. + */ + public boolean isSharedMapSupported() { + return mSharedMapSupported; + } + + /** + * Sets shared map as supported or not supported. + */ + public void setSharedMapSupported(boolean sharedMapSupported) { + this.mSharedMapSupported = sharedMapSupported; + } + + /** + * Checks whether shared sketch is supported. + */ + public boolean isSharedSketchSupported() { + return mSharedSketchSupported; + } + + /** + * Sets shared sketch as supported or not supported. + */ + public void setSharedSketchSupported(boolean sharedSketchSupported) { + this.mSharedSketchSupported = sharedSketchSupported; + } + + /** + * Checks whether chatbot communication is supported. + */ + public boolean isChatbotSupported() { + return mChatbotSupported; + } + + /** + * Sets chatbot communication as supported or not supported. + */ + public void setChatbotSupported(boolean chatbotSupported) { + this.mChatbotSupported = chatbotSupported; + } + + /** + * Checks whether chatbot role is supported. + */ + public boolean isChatbotRoleSupported() { + return mChatbotRoleSupported; + } + + /** + * Sets chatbot role as supported or not supported. + */ + public void setChatbotRoleSupported(boolean chatbotRoleSupported) { + this.mChatbotRoleSupported = chatbotRoleSupported; + } + /** Gets the list of supported extensions. */ public String[] getExts() { return mExts; @@ -434,6 +546,13 @@ public class CapInfo implements Parcelable { dest.writeInt(mGeoPushSupported ? 1 : 0); dest.writeInt(mSmSupported ? 1 : 0); dest.writeInt(mFullSnFGroupChatSupported ? 1 : 0); + dest.writeInt(mGeoSmsSupported ? 1 : 0); + dest.writeInt(mCallComposerSupported ? 1 : 0); + dest.writeInt(mPostCallSupported ? 1 : 0); + dest.writeInt(mSharedMapSupported ? 1 : 0); + dest.writeInt(mSharedSketchSupported ? 1 : 0); + dest.writeInt(mChatbotSupported ? 1 : 0); + dest.writeInt(mChatbotRoleSupported ? 1 : 0); dest.writeInt(mRcsIpVoiceCallSupported ? 1 : 0); dest.writeInt(mRcsIpVideoCallSupported ? 1 : 0); @@ -476,6 +595,13 @@ public class CapInfo implements Parcelable { mGeoPushSupported = (source.readInt() == 0) ? false : true; mSmSupported = (source.readInt() == 0) ? false : true; mFullSnFGroupChatSupported = (source.readInt() == 0) ? false : true; + mGeoSmsSupported = (source.readInt() == 0) ? false : true; + mCallComposerSupported = (source.readInt() == 0) ? false : true; + mPostCallSupported = (source.readInt() == 0) ? false : true; + mSharedMapSupported = (source.readInt() == 0) ? false : true; + mSharedSketchSupported = (source.readInt() == 0) ? false : true; + mChatbotSupported = (source.readInt() == 0) ? false : true; + mChatbotRoleSupported = (source.readInt() == 0) ? false : true; mRcsIpVoiceCallSupported = (source.readInt() == 0) ? false : true; mRcsIpVideoCallSupported = (source.readInt() == 0) ? false : true; diff --git a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java index a50a22f68fa0..fdff86f9669f 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java +++ b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java @@ -47,6 +47,10 @@ public class PresPublishTriggerType implements Parcelable { public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN = 8; /** Trigger is unknown. */ public static final int UCE_PRES_PUBLISH_TRIGGER_UNKNOWN = 9; + /** Move to 5G NR with VoPS disabled. */ + public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; + /** Move to 5G NR with VoPS enabled. */ + public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; @@ -113,4 +117,4 @@ public class PresPublishTriggerType implements Parcelable { public void readFromParcel(Parcel source) { mPublishTriggerType = source.readInt(); } -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 9e23c28a0711..9fbc1b74c9ae 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -350,7 +350,12 @@ public final class SystemUiDeviceConfigFlags { * (boolean) Whether screenshot flow going to the corner (instead of shown in a notification) * is enabled. */ - public static final String SCREENSHOT_CORNER_FLOW = "screenshot_corner_flow"; + public static final String SCREENSHOT_CORNER_FLOW = "enable_screenshot_corner_flow"; + + /** + * (boolean) Whether scrolling screenshots are enabled. + */ + public static final String SCREENSHOT_SCROLLING_ENABLED = "enable_screenshot_scrolling"; private SystemUiDeviceConfigFlags() { } diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/ConnectivityUtil.java new file mode 100644 index 000000000000..799352b9ec15 --- /dev/null +++ b/core/java/com/android/internal/util/ConnectivityUtil.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.util; + +import android.Manifest; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.location.LocationManager; +import android.os.Binder; +import android.os.Build; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + + +/** + * Utility methods for common functionality using by different networks. + * + * @hide + */ +public class ConnectivityUtil { + + private static final String TAG = "ConnectivityUtil"; + + private final Context mContext; + private final AppOpsManager mAppOps; + private final UserManager mUserManager; + + public ConnectivityUtil(Context context) { + mContext = context; + mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + } + + /** + * API to determine if the caller has fine/coarse location permission (depending on + * config/targetSDK level) and the location mode is enabled for the user. SecurityException is + * thrown if the caller has no permission or the location mode is disabled. + * @param pkgName package name of the application requesting access + * @param featureId The feature in the package + * @param uid The uid of the package + * @param message A message describing why the permission was checked. Only needed if this is + * not inside of a two-way binder call from the data receiver + */ + public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid, + @Nullable String message) + throws SecurityException { + checkPackage(uid, pkgName); + + // Location mode must be enabled + if (!isLocationModeEnabled()) { + // Location mode is disabled, scan results cannot be returned + throw new SecurityException("Location mode is disabled for the device"); + } + + // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to + // location information. + boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId, + uid, /* coarseForTargetSdkLessThanQ */ true, message); + + // If neither caller or app has location access, there is no need to check + // any other permissions. Deny access to scan results. + if (!canAppPackageUseLocation) { + throw new SecurityException("UID " + uid + " has no location permission"); + } + // If the User or profile is current, permission is granted + // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. + if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { + throw new SecurityException("UID " + uid + " profile not permitted"); + } + } + + /** + * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or + * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level) + * and a corresponding app op is allowed for this package and uid. + * + * @param pkgName PackageName of the application requesting access + * @param featureId The feature in the package + * @param uid The uid of the package + * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE + * else (false or targetSDK >= Q) then will check for FINE + * @param message A message describing why the permission was checked. Only needed if this is + * not inside of a two-way binder call from the data receiver + */ + public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId, + int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) { + boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid); + + String permissionType = Manifest.permission.ACCESS_FINE_LOCATION; + if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) { + // Having FINE permission implies having COARSE permission (but not the reverse) + permissionType = Manifest.permission.ACCESS_COARSE_LOCATION; + } + if (getUidPermission(permissionType, uid) + == PackageManager.PERMISSION_DENIED) { + return false; + } + + // Always checking FINE - even if will not enforce. This will record the request for FINE + // so that a location request by the app is surfaced to the user. + boolean isFineLocationAllowed = noteAppOpAllowed( + AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message); + if (isFineLocationAllowed) { + return true; + } + if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) { + return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid, + message); + } + return false; + } + + /** + * Retrieves a handle to LocationManager (if not already done) and check if location is enabled. + */ + public boolean isLocationModeEnabled() { + LocationManager locationManager = + (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); + try { + return locationManager.isLocationEnabledForUser(UserHandle.of( + getCurrentUser())); + } catch (Exception e) { + Log.e(TAG, "Failure to get location mode via API, falling back to settings", e); + return false; + } + } + + private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) { + long ident = Binder.clearCallingIdentity(); + try { + if (mContext.getPackageManager().getApplicationInfoAsUser( + packageName, 0, + UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion + < versionCode) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // In case of exception, assume unknown app (more strict checking) + // Note: This case will never happen since checkPackage is + // called to verify validity before checking App's version. + } finally { + Binder.restoreCallingIdentity(ident); + } + return false; + } + + private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId, + int uid, @Nullable String message) { + return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED; + } + + private void checkPackage(int uid, String pkgName) throws SecurityException { + if (pkgName == null) { + throw new SecurityException("Checking UID " + uid + " but Package Name is Null"); + } + mAppOps.checkPackage(uid, pkgName); + } + + private boolean isCurrentProfile(int uid) { + UserHandle currentUser = UserHandle.of(getCurrentUser()); + UserHandle callingUser = UserHandle.getUserHandleForUid(uid); + return currentUser.equals(callingUser) + || mUserManager.isSameProfileGroup(currentUser, callingUser); + } + + private boolean checkInteractAcrossUsersFull(int uid) { + return getUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid) + == PackageManager.PERMISSION_GRANTED; + } + + @VisibleForTesting + protected int getCurrentUser() { + return ActivityManager.getCurrentUser(); + } + + private int getUidPermission(String permissionType, int uid) { + // We don't care about pid, pass in -1 + return mContext.checkPermission(permissionType, -1, uid); + } +} diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java index 43a227a32346..d7a01c4762f1 100644 --- a/core/java/com/android/internal/widget/RecyclerView.java +++ b/core/java/com/android/internal/widget/RecyclerView.java @@ -2011,13 +2011,27 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro } if (!dispatchNestedPreFling(velocityX, velocityY)) { - final boolean canScroll = canScrollHorizontal || canScrollVertical; - dispatchNestedFling(velocityX, velocityY, canScroll); + final View firstChild = mLayout.getChildAt(0); + final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1); + boolean consumed = false; + if (velocityY < 0) { + consumed = getChildAdapterPosition(firstChild) > 0 + || firstChild.getTop() < getPaddingTop(); + } + + if (velocityY > 0) { + consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1 + || lastChild.getBottom() > getHeight() - getPaddingBottom(); + } + + dispatchNestedFling(velocityX, velocityY, consumed); if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) { return true; } + final boolean canScroll = canScrollHorizontal || canScrollVertical; + if (canScroll) { velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity)); velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity)); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index ff596b440867..062b886f54a1 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -220,8 +220,12 @@ static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& valu // ---------------------------------------------------------------------------- +static std::unique_ptr<DynamicLibManager> sDynamicLibManager = + std::make_unique<DynamicLibManager>(); + // Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. struct GuardedAssetManager : public ::AAssetManager { + GuardedAssetManager() : guarded_assetmanager(sDynamicLibManager.get()) {} Guarded<AssetManager2> guarded_assetmanager; }; diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 0992bebb5be0..fb8e633fec12 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -537,9 +537,9 @@ public: LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this); if (mObject != NULL) { JNIEnv* env = javavm_to_jnienv(mVM); - + jobject jBinderProxy = javaObjectForIBinder(env, who.promote()); env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, - gBinderProxyOffsets.mSendDeathNotice, mObject); + gBinderProxyOffsets.mSendDeathNotice, mObject, jBinderProxy); if (env->ExceptionCheck()) { jthrowable excep = env->ExceptionOccurred(); report_exception(env, excep, @@ -1532,8 +1532,9 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz); gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance", "(JJ)Landroid/os/BinderProxy;"); - gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice", - "(Landroid/os/IBinder$DeathRecipient;)V"); + gBinderProxyOffsets.mSendDeathNotice = + GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice", + "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V"); gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J"); clazz = FindClassOrDie(env, "java/lang/Class"); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 4a7276c4f94e..b47080f787a1 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -263,7 +263,8 @@ static jobject nativeScreenshot(JNIEnv* env, jclass clazz, status_t res = ScreenshotClient::capture(displayToken, dataspace, ui::PixelFormat::RGBA_8888, sourceCrop, width, height, - useIdentityTransform, rotation, captureSecureLayers, &buffer, capturedSecureLayers); + useIdentityTransform, ui::toRotation(rotation), + captureSecureLayers, &buffer, capturedSecureLayers); if (res != NO_ERROR) { return NULL; } @@ -724,7 +725,8 @@ static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz, { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - transaction->setDisplayProjection(token, orientation, layerStackRect, displayRect); + transaction->setDisplayProjection(token, static_cast<ui::Rotation>(orientation), + layerStackRect, displayRect); } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 5039213954d7..466544c1448e 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -746,9 +746,6 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, const userid_t user_id = multiuser_get_user_id(uid); const std::string user_source = StringPrintf("/mnt/user/%d", user_id); - const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id); - bool isFuse = GetBoolProperty(kPropFuse, false); - // Shell is neither AID_ROOT nor AID_EVERYBODY. Since it equally needs 'execute' access to // /mnt/user/0 to 'adb shell ls /sdcard' for instance, we set the uid bit of /mnt/user/0 to // AID_SHELL. This gives shell access along with apps running as group everybody (user 0 apps) @@ -757,9 +754,15 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL, multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn); + bool isFuse = GetBoolProperty(kPropFuse, false); + if (isFuse) { if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { + const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id); BindMount(pass_through_source, "/storage", fail_fn); + } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { + const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id); + BindMount(installer_source, "/storage", fail_fn); } else { BindMount(user_source, "/storage", fail_fn); } diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 9054d5462da5..0fca1d19c0e5 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -154,4 +154,5 @@ enum EventId { SET_AUTO_TIME = 127; SET_AUTO_TIME_ZONE = 128; SET_PACKAGES_PROTECTED = 129; + SET_FACTORY_RESET_PROTECTION = 130; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c050146c0e1a..b78b18192f45 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -445,6 +445,7 @@ <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" /> <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" /> + <protected-broadcast android:name="android.app.action.RESET_PROTECTION_POLICY_CHANGED" /> <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" /> <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" /> @@ -640,10 +641,6 @@ <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" /> - <!-- NETWORK_SET_TIME moved from com.android.phone to system server. It should ultimately be - removed. --> - <protected-broadcast android:name="android.telephony.action.NETWORK_SET_TIME" /> - <!-- For tether entitlement recheck--> <protected-broadcast android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" /> @@ -1791,6 +1788,11 @@ android:label="@string/permlab_preferredPaymentInfo" android:protectionLevel="normal" /> + <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs. + @hide --> + <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED" + android:protectionLevel="signature|privileged" /> + <!-- @deprecated This permission used to allow too broad access to sensitive methods and all its uses have been replaced by a more appropriate permission. Most uses have been replaced with a NETWORK_STACK or NETWORK_SETTINGS check. Please look up the documentation of the @@ -3384,6 +3386,14 @@ <permission android:name="android.permission.NOTIFY_TV_INPUTS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to interact with tuner resources through + Tuner Resource Manager. + <p>Protection level: signature|privileged + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.TUNER_RESOURCE_ACCESS" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.media.routing.MediaRouteService} to ensure that only the system can interact with it. @hide --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6691d4c5955b..6f554f0264df 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1877,6 +1877,8 @@ <string name="config_defaultCallRedirection" translatable="false"></string> <!-- The name of the package that will hold the call screening role by default. --> <string name="config_defaultCallScreening" translatable="false"></string> + <!-- The name of the package that will hold the system gallery role. --> + <string name="config_systemGallery" translatable="false">com.android.gallery</string> <!-- Enable/disable default bluetooth profiles: HSP_AG, ObexObjectPush, Audio, NAP --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 37c971021b2e..6cf6a6828237 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3030,6 +3030,8 @@ <public name="config_defaultCallRedirection" /> <!-- @hide @SystemApi --> <public name="config_defaultCallScreening" /> + <!-- @hide @SystemApi @TestApi --> + <public name="config_systemGallery" /> </public-group> <public-group type="bool" first-id="0x01110005"> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index caae9088e9b5..2df6d1ca0e2d 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -8,6 +8,7 @@ android_test { "EnabledTestApp/src/**/*.java", "BinderProxyCountingTestApp/src/**/*.java", "BinderProxyCountingTestService/src/**/*.java", + "BinderDeathRecipientHelperApp/src/**/*.java", "aidl/**/I*.aidl", ], @@ -59,7 +60,11 @@ android_test { resource_dirs: ["res"], resource_zips: [":FrameworksCoreTests_apks_as_resources"], - data: [":BstatsTestApp"], + data: [ + ":BstatsTestApp", + ":BinderDeathRecipientHelperApp1", + ":BinderDeathRecipientHelperApp2", + ], } // Rules to copy all the test apks to the intermediate raw resource directory diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 1aea98a93afe..b85a332e9000 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -95,6 +95,7 @@ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> <uses-permission android:name="android.permission.KILL_UID" /> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" /> <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index b40aa87cb78b..ed9d3f54ef3d 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -21,6 +21,8 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="FrameworksCoreTests.apk" /> <option name="test-file-name" value="BstatsTestApp.apk" /> + <option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" /> + <option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" /> </target_preparer> <option name="test-tag" value="FrameworksCoreTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp new file mode 100644 index 000000000000..25e4fc366124 --- /dev/null +++ b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp @@ -0,0 +1,19 @@ +android_test_helper_app { + name: "BinderDeathRecipientHelperApp1", + + srcs: ["**/*.java"], + + sdk_version: "current", +} + +android_test_helper_app { + name: "BinderDeathRecipientHelperApp2", + + srcs: ["**/*.java"], + + sdk_version: "current", + + aaptflags: [ + "--rename-manifest-package com.android.frameworks.coretests.bdr_helper_app2", + ], +} diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml b/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml new file mode 100644 index 000000000000..dbd1774b7d79 --- /dev/null +++ b/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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.frameworks.coretests.bdr_helper_app1"> + + <application> + <receiver android:name="com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver" + android:exported="true"/> + + </application> +</manifest> diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java b/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java new file mode 100644 index 000000000000..ab79e69d1351 --- /dev/null +++ b/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.frameworks.coretests.bdr_helper_app; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.util.Log; + +/** + * Receiver used to hand off a binder owned by this process to + * {@link android.os.BinderDeathRecipientTest}. + */ +public class TestCommsReceiver extends BroadcastReceiver { + private static final String TAG = TestCommsReceiver.class.getSimpleName(); + private static final String PACKAGE_NAME = "com.android.frameworks.coretests.bdr_helper_app"; + + public static final String ACTION_GET_BINDER = PACKAGE_NAME + ".action.GET_BINDER"; + public static final String EXTRA_KEY_BINDER = PACKAGE_NAME + ".EXTRA_BINDER"; + + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case ACTION_GET_BINDER: + final Bundle resultExtras = new Bundle(); + resultExtras.putBinder(EXTRA_KEY_BINDER, new Binder()); + setResult(Activity.RESULT_OK, null, resultExtras); + break; + default: + Log.e(TAG, "Unknown action " + intent.getAction()); + break; + } + } +} diff --git a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java new file mode 100644 index 000000000000..2cce43f70774 --- /dev/null +++ b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.ArraySet; +import android.util.Log; +import android.util.Pair; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Tests functionality of {@link android.os.IBinder.DeathRecipient} callbacks. + */ +@RunWith(AndroidJUnit4.class) +public class BinderDeathRecipientTest { + private static final String TAG = BinderDeathRecipientTest.class.getSimpleName(); + private static final String TEST_PACKAGE_NAME_1 = + "com.android.frameworks.coretests.bdr_helper_app1"; + private static final String TEST_PACKAGE_NAME_2 = + "com.android.frameworks.coretests.bdr_helper_app2"; + + private Context mContext; + private Handler mHandler; + private ActivityManager mActivityManager; + private Set<Pair<IBinder, IBinder.DeathRecipient>> mLinkedDeathRecipients = new ArraySet<>(); + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + mActivityManager = mContext.getSystemService(ActivityManager.class); + mHandler = new Handler(Looper.getMainLooper()); + } + + private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException { + final CountDownLatch resultLatch = new CountDownLatch(1); + final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED); + final AtomicReference<Bundle> resultExtras = new AtomicReference<>(); + + final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER) + .setClassName(testPackage, TestCommsReceiver.class.getName()); + mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + resultCode.set(getResultCode()); + resultExtras.set(getResultExtras(true)); + resultLatch.countDown(); + } + }, mHandler, Activity.RESULT_CANCELED, null, null); + + assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS)); + assertEquals(Activity.RESULT_OK, resultCode.get()); + return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER); + } + + @Test + public void binderDied_noArgs() throws Exception { + final IBinder testAppBinder = getNewRemoteBinder(TEST_PACKAGE_NAME_1); + final CountDownLatch deathNotificationLatch = new CountDownLatch(1); + final IBinder.DeathRecipient simpleDeathRecipient = + () -> deathNotificationLatch.countDown(); + testAppBinder.linkToDeath(simpleDeathRecipient, 0); + mLinkedDeathRecipients.add(Pair.create(testAppBinder, simpleDeathRecipient)); + + mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1); + assertTrue("Death notification did not arrive", + deathNotificationLatch.await(10, TimeUnit.SECONDS)); + } + + @Test + public void binderDied_iBinderArg() throws Exception { + final IBinder testApp1Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_1); + final IBinder testApp2Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_2); + final CyclicBarrier barrier = new CyclicBarrier(2); + + final AtomicReference<IBinder> binderThatDied = new AtomicReference<>(); + final IBinder.DeathRecipient sameDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.e(TAG, "Should not have been called!"); + } + + @Override + public void binderDied(IBinder who) { + binderThatDied.set(who); + try { + barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + Log.e(TAG, "Unexpected exception while waiting on CyclicBarrier", e); + } + } + }; + testApp1Binder.linkToDeath(sameDeathRecipient, 0); + mLinkedDeathRecipients.add(Pair.create(testApp1Binder, sameDeathRecipient)); + + testApp2Binder.linkToDeath(sameDeathRecipient, 0); + mLinkedDeathRecipients.add(Pair.create(testApp2Binder, sameDeathRecipient)); + + mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1); + try { + barrier.await(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + fail("Timed out while waiting for 1st death notification: " + e.getMessage()); + } + assertEquals("Different binder received", testApp1Binder, binderThatDied.get()); + + barrier.reset(); + mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_2); + try { + barrier.await(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + fail("Timed out while waiting for 2nd death notification: " + e.getMessage()); + } + assertEquals("Different binder received", testApp2Binder, binderThatDied.get()); + } + + @After + public void tearDown() { + for (Pair<IBinder, IBinder.DeathRecipient> linkedPair : mLinkedDeathRecipients) { + linkedPair.first.unlinkToDeath(linkedPair.second, 0); + } + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 3586216ad421..93f4b5143c57 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -124,6 +124,10 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void setSoftKeyboardCallbackEnabled(boolean enabled) {} + public boolean switchToInputMethod(String imeId) { + return false; + } + public boolean isAccessibilityButtonAvailable() { return false; } diff --git a/core/tests/overlaytests/remount/TEST_MAPPING b/core/tests/overlaytests/remount/TEST_MAPPING new file mode 100644 index 000000000000..54dd4310bd6e --- /dev/null +++ b/core/tests/overlaytests/remount/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name" : "OverlayRemountedTest" + } + ] +}
\ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/Android.bp b/core/tests/overlaytests/remount/host/Android.bp new file mode 100644 index 000000000000..3825c55f3b4b --- /dev/null +++ b/core/tests/overlaytests/remount/host/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_test_host { + name: "OverlayRemountedTest", + srcs: ["src/**/*.java"], + libs: [ + "tradefed", + "junit", + ], + test_suites: ["general-tests"], + java_resources: [ + ":OverlayRemountedTest_SharedLibrary", + ":OverlayRemountedTest_SharedLibraryOverlay", + ":OverlayRemountedTest_Target", + ], +} diff --git a/core/tests/overlaytests/remount/host/AndroidTest.xml b/core/tests/overlaytests/remount/host/AndroidTest.xml new file mode 100644 index 000000000000..11eadf1a4659 --- /dev/null +++ b/core/tests/overlaytests/remount/host/AndroidTest.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. + --> + +<configuration description="Test module config for OverlayRemountedTest"> + <option name="test-tag" value="OverlayRemountedTest" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="remount" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.HostTest"> + <option name="jar" value="OverlayRemountedTest.jar" /> + </test> +</configuration> diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java new file mode 100644 index 000000000000..84af18710fe6 --- /dev/null +++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.overlaytest.remounted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.Rule; +import org.junit.rules.RuleChain; +import org.junit.rules.TemporaryFolder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OverlayHostTest extends BaseHostJUnit4Test { + private static final long TIME_OUT_MS = 30000; + private static final String RES_INSTRUMENTATION_ARG = "res"; + private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays"; + private static final String RESOURCES_TYPE_SUFFIX = "_type"; + private static final String RESOURCES_DATA_SUFFIX = "_data"; + + public final TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice); + + @Rule + public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer); + private Map<String, String> mLastResults; + + /** + * Retrieves the values of the resources in the test package. The test package must use the + * {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation. + **/ + void retrieveResource(String testPackageName, List<String> requiredOverlayPaths, + String... resourceNames) throws DeviceNotAvailableException { + final HashMap<String, String> args = new HashMap<>(); + if (!requiredOverlayPaths.isEmpty()) { + // Enclose the require overlay paths in quotes so the arguments will be string arguments + // rather than file arguments. + args.put(OVERLAY_INSTRUMENTATION_ARG, + String.format("\"%s\"", String.join(" ", requiredOverlayPaths))); + } + + if (resourceNames.length == 0) { + throw new IllegalArgumentException("Must specify at least one resource to retrieve."); + } + + // Pass the names of the resources to retrieve into the test as one string. + args.put(RES_INSTRUMENTATION_ARG, + String.format("\"%s\"", String.join(" ", resourceNames))); + + runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS, + TIME_OUT_MS, TIME_OUT_MS, false, false, args); + + // Retrieve the results of the most recently run test. + mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null : + getLastDeviceRunResults().getRunMetrics(); + } + + /** Returns the base resource directories of the specified packages. */ + List<String> getPackagePaths(String... packageNames) + throws DeviceNotAvailableException { + final ArrayList<String> paths = new ArrayList<>(); + for (String packageName : packageNames) { + // Use the package manager shell command to find the path of the package. + final String result = getDevice().executeShellCommand( + String.format("pm dump %s | grep \"resourcePath=\"", packageName)); + assertNotNull("Failed to find path for package " + packageName, result); + int splitIndex = result.indexOf('='); + assertTrue(splitIndex >= 0); + paths.add(result.substring(splitIndex + 1).trim()); + } + return paths; + } + + /** Builds the full name of a resource in the form package:type/entry. */ + String resourceName(String pkg, String type, String entry) { + return String.format("%s:%s/%s", pkg, type, entry); + } + + /** + * Asserts that the type and data of a a previously retrieved is the same as expected. + * @param resourceName the full name of the resource in the form package:type/entry + * @param type the expected {@link android.util.TypedValue} type of the resource + * @param data the expected value of the resource when coerced to a string using + * {@link android.util.TypedValue#coerceToString()} + **/ + void assertResource(String resourceName, int type, String data) { + assertNotNull("Failed to get test results", mLastResults); + assertNotEquals("No resource values were retrieved", mLastResults.size(), 0); + assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX)); + assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX)); + } +} diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java new file mode 100644 index 000000000000..4939e160612e --- /dev/null +++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java @@ -0,0 +1,90 @@ +/* + * 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.overlaytest.remounted; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.List; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class OverlaySharedLibraryTest extends OverlayHostTest { + private static final String TARGET_APK = "OverlayRemountedTest_Target.apk"; + private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target"; + private static final String SHARED_LIBRARY_APK = + "OverlayRemountedTest_SharedLibrary.apk"; + private static final String SHARED_LIBRARY_PACKAGE = + "com.android.overlaytest.remounted.shared_library"; + private static final String SHARED_LIBRARY_OVERLAY_APK = + "OverlayRemountedTest_SharedLibraryOverlay.apk"; + private static final String SHARED_LIBRARY_OVERLAY_PACKAGE = + "com.android.overlaytest.remounted.shared_library.overlay"; + + @Test + public void testSharedLibrary() throws Exception { + final String targetResource = resourceName(TARGET_PACKAGE, "bool", + "uses_shared_library_overlaid"); + final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool", + "shared_library_overlaid"); + + mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk") + .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE) + .reboot() + .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false) + .installResourceApk(TARGET_APK, TARGET_PACKAGE); + + // The shared library resource is not currently overlaid. + retrieveResource(Collections.emptyList(), targetResource, libraryResource); + assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false"); + assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false"); + + // Overlay the shared library resource. + mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true); + retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource, + libraryResource); + assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + } + + @Test + public void testSharedLibraryPreEnabled() throws Exception { + final String targetResource = resourceName(TARGET_PACKAGE, "bool", + "uses_shared_library_overlaid"); + final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool", + "shared_library_overlaid"); + + mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk") + .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE) + .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true) + .reboot() + .installResourceApk(TARGET_APK, TARGET_PACKAGE); + + retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource, + libraryResource); + assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + } + + private void retrieveResource(List<String> requiredOverlayPaths, String... resourceNames) + throws DeviceNotAvailableException { + retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames); + } +} diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java new file mode 100644 index 000000000000..7028f2f1d554 --- /dev/null +++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java @@ -0,0 +1,158 @@ +/* + * 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.overlaytest.remounted; + +import static org.junit.Assert.assertTrue; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; + +import org.junit.Assert; +import org.junit.rules.ExternalResource; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeoutException; + +class SystemPreparer extends ExternalResource { + private static final long REBOOT_SLEEP_MS = 30000; + private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000; + + // The paths of the files pushed onto the device through this rule. + private ArrayList<String> mPushedFiles = new ArrayList<>(); + + // The package names of packages installed through this rule. + private ArrayList<String> mInstalledPackages = new ArrayList<>(); + + private final TemporaryFolder mHostTempFolder; + private final DeviceProvider mDeviceProvider; + + SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) { + mHostTempFolder = hostTempFolder; + mDeviceProvider = deviceProvider; + } + + /** Copies a file within the host test jar to a path on device. */ + SystemPreparer pushResourceFile(String resourcePath, + String outputPath) throws DeviceNotAvailableException, IOException { + final ITestDevice device = mDeviceProvider.getDevice(); + assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath)); + mPushedFiles.add(outputPath); + return this; + } + + /** Installs an APK within the host test jar onto the device. */ + SystemPreparer installResourceApk(String resourcePath, String packageName) + throws DeviceNotAvailableException, IOException { + final ITestDevice device = mDeviceProvider.getDevice(); + final File tmpFile = copyResourceToTemp(resourcePath); + final String result = device.installPackage(tmpFile, true); + Assert.assertNull(result); + mInstalledPackages.add(packageName); + return this; + } + + /** Sets the enable state of an overlay pacakage. */ + SystemPreparer setOverlayEnabled(String packageName, boolean enabled) + throws ExecutionException, TimeoutException { + final ITestDevice device = mDeviceProvider.getDevice(); + + // Wait for the overlay to change its enabled state. + final FutureTask<Boolean> enabledListener = new FutureTask<>(() -> { + while (true) { + device.executeShellCommand(String.format("cmd overlay %s %s", + enabled ? "enable" : "disable", packageName)); + + final String pattern = (enabled ? "[x]" : "[ ]") + " " + packageName; + if (device.executeShellCommand("cmd overlay list").contains(pattern)) { + return true; + } + } + }); + + final Executor executor = (cmd) -> new Thread(cmd).start(); + executor.execute(enabledListener); + try { + enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS); + } catch (InterruptedException ignored) { + } + + return this; + } + + /** Restarts the device and waits until after boot is completed. */ + SystemPreparer reboot() throws DeviceNotAvailableException { + final ITestDevice device = mDeviceProvider.getDevice(); + device.executeShellCommand("stop"); + device.executeShellCommand("start"); + try { + // Sleep until the device is ready for test execution. + Thread.sleep(REBOOT_SLEEP_MS); + } catch (InterruptedException ignored) { + } + + return this; + } + + /** Copies a file within the host test jar to a temporary file on the host machine. */ + private File copyResourceToTemp(String resourcePath) throws IOException { + final File tempFile = mHostTempFolder.newFile(resourcePath); + final ClassLoader classLoader = getClass().getClassLoader(); + try (InputStream assetIs = classLoader.getResource(resourcePath).openStream(); + FileOutputStream assetOs = new FileOutputStream(tempFile)) { + if (assetIs == null) { + throw new IllegalStateException("Failed to find resource " + resourcePath); + } + + int b; + while ((b = assetIs.read()) >= 0) { + assetOs.write(b); + } + } + + return tempFile; + } + + /** Removes installed packages and files that were pushed to the device. */ + @Override + protected void after() { + final ITestDevice device = mDeviceProvider.getDevice(); + try { + for (final String file : mPushedFiles) { + device.deleteFile(file); + } + for (final String packageName : mInstalledPackages) { + device.uninstallPackage(packageName); + } + } catch (DeviceNotAvailableException e) { + Assert.fail(e.toString()); + } + } + + interface DeviceProvider { + ITestDevice getDevice(); + } +} diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp new file mode 100644 index 000000000000..ffb0572c3fd0 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp @@ -0,0 +1,19 @@ +// 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. + +android_test_helper_app { + name: "OverlayRemountedTest_SharedLibrary", + sdk_version: "current", + aaptflags: ["--shared-lib"], +} diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml new file mode 100644 index 000000000000..06e3f6a99410 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?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="com.android.overlaytest.remounted.shared_library"> + <application> + <library android:name="com.android.overlaytest.remounted.shared_library" /> + </application> +</manifest>
\ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml new file mode 100644 index 000000000000..1b06f6d7530b --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <overlayable name="TestResources"> + <policy type="public"> + <item type="bool" name="shared_library_overlaid" /> + </policy> + </overlayable> +</resources> diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml new file mode 100644 index 000000000000..5b9db163a274 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml @@ -0,0 +1,20 @@ +<?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> + <public type="bool" name="shared_library_overlaid" id="0x00050001"/> +</resources>
\ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml new file mode 100644 index 000000000000..2dc47a7ecf61 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml @@ -0,0 +1,20 @@ +<?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> + <bool name="shared_library_overlaid">false</bool> +</resources> diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp new file mode 100644 index 000000000000..0d29aec909d5 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp @@ -0,0 +1,18 @@ +// 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. + +android_test_helper_app { + name: "OverlayRemountedTest_SharedLibraryOverlay", + sdk_version: "current", +} diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..53a4e61949da --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?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="com.android.overlaytest.remounted.shared_library.overlay"> + <application android:hasCode="false" /> + <overlay android:targetPackage="com.android.overlaytest.remounted.shared_library" + android:targetName="TestResources" /> +</manifest>
\ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml new file mode 100644 index 000000000000..f66448a8d991 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml @@ -0,0 +1,20 @@ +<?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> + <bool name="shared_library_overlaid">true</bool> +</resources> diff --git a/core/tests/overlaytests/remount/target/Android.bp b/core/tests/overlaytests/remount/target/Android.bp new file mode 100644 index 000000000000..83f9f28b3f48 --- /dev/null +++ b/core/tests/overlaytests/remount/target/Android.bp @@ -0,0 +1,20 @@ +// 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. + +android_test_helper_app { + name: "OverlayRemountedTest_Target", + srcs: ["src/**/*.java"], + sdk_version: "test_current", + libs: ["OverlayRemountedTest_SharedLibrary"], +} diff --git a/core/tests/overlaytests/remount/target/AndroidManifest.xml b/core/tests/overlaytests/remount/target/AndroidManifest.xml new file mode 100644 index 000000000000..32fec43593f8 --- /dev/null +++ b/core/tests/overlaytests/remount/target/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.overlaytest.remounted.target"> + + <application> + <uses-library android:name="android.test.runner" /> + <uses-library android:name="com.android.overlaytest.remounted.shared_library" + android:required="true" /> + </application> + + <instrumentation android:name="com.android.overlaytest.remounted.target.ResourceRetrievalRunner" + android:targetPackage="com.android.overlaytest.remounted.target" + android:label="Remounted system RRO tests" /> +</manifest> diff --git a/core/tests/overlaytests/remount/target/res/values/values.xml b/core/tests/overlaytests/remount/target/res/values/values.xml new file mode 100644 index 000000000000..b5f444a5eb72 --- /dev/null +++ b/core/tests/overlaytests/remount/target/res/values/values.xml @@ -0,0 +1,20 @@ +<?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 xmlns:sharedlib="http://schemas.android.com/apk/res/com.android.overlaytest.remounted.shared_library"> + <bool name="uses_shared_library_overlaid">@sharedlib:bool/shared_library_overlaid</bool> +</resources> diff --git a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java new file mode 100644 index 000000000000..2e4c211d6a0a --- /dev/null +++ b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java @@ -0,0 +1,140 @@ +/* + * 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.overlaytest.remounted.target; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.res.Resources; +import android.os.Bundle; +import android.util.Log; +import android.util.TypedValue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +/** + * An {@link Instrumentation} that retrieves the value of specified resources within the + * application. + **/ +public class ResourceRetrievalRunner extends Instrumentation { + private static final String TAG = ResourceRetrievalRunner.class.getSimpleName(); + + // A list of whitespace separated resource names of which to retrieve the resource values. + private static final String RESOURCE_LIST_TAG = "res"; + + // A list of whitespace separated overlay package paths that must be present before retrieving + // resource values. + private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays"; + + // The suffixes of the keys returned from the instrumentation. To retrieve the type of a + // resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix + // to the end of the name of the resource. For the value of a resource, use + // {@link #RESOURCES_DATA_SUFFIX} instead. + private static final String RESOURCES_TYPE_SUFFIX = "_type"; + private static final String RESOURCES_DATA_SUFFIX = "_data"; + + // The amount of time in seconds to wait for the overlays to be present in the AssetManager. + private static final int OVERLAY_PATH_TIMEOUT = 60; + + private final ArrayList<String> mResourceNames = new ArrayList<>(); + private final ArrayList<String> mOverlayPaths = new ArrayList<>(); + private final Bundle mResult = new Bundle(); + + /** + * Receives the instrumentation arguments and runs the resource retrieval. + * The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a + * whitespace separated string of resource names of which to retrieve the resource values. + * The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a + * whitespace separated string of overlay package paths prefixes that must be present before + * retrieving the resource values. + */ + @Override + public void onCreate(Bundle arguments) { + super.onCreate(arguments); + mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" "))); + if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) { + mOverlayPaths.addAll(Arrays.asList( + arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" "))); + } + start(); + } + + @Override + public void onStart() { + final Resources res = getContext().getResources(); + res.getAssets().setResourceResolutionLoggingEnabled(true); + + if (!mOverlayPaths.isEmpty()) { + Log.d(TAG, String.format("Waiting for overlay paths [%s]", + String.join(",", mOverlayPaths))); + + // Wait for all required overlays to be present in the AssetManager. + final FutureTask<Boolean> overlayListener = new FutureTask<>(() -> { + while (!mOverlayPaths.isEmpty()) { + final String[] apkPaths = res.getAssets().getApkPaths(); + for (String path : apkPaths) { + for (String overlayPath : mOverlayPaths) { + if (path.startsWith(overlayPath)) { + mOverlayPaths.remove(overlayPath); + break; + } + } + } + } + return true; + }); + + try { + final Executor executor = (t) -> new Thread(t).start(); + executor.execute(overlayListener); + overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS); + } catch (Exception e) { + Log.e(TAG, String.format("Failed to wait for required overlays [%s]", + String.join(",", mOverlayPaths)), e); + finish(Activity.RESULT_CANCELED, mResult); + } + } + + // Retrieve the values for each resource passed in. + final TypedValue typedValue = new TypedValue(); + for (final String resourceName : mResourceNames) { + try { + final int resId = res.getIdentifier(resourceName, null, null); + res.getValue(resId, typedValue, true); + Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId), + res.getAssets().getLastResourceResolution())); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e); + finish(Activity.RESULT_CANCELED, mResult); + } + + putValue(resourceName, typedValue); + } + + finish(Activity.RESULT_OK, mResult); + } + + private void putValue(String resourceName, TypedValue value) { + mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type); + final CharSequence textValue = value.coerceToString(); + mResult.putString(resourceName + RESOURCES_DATA_SUFFIX, + textValue == null ? "null" : textValue.toString()); + } +} diff --git a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java new file mode 100644 index 000000000000..0809f693ddd5 --- /dev/null +++ b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.util; + +import static org.junit.Assert.assertTrue; +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.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.location.LocationManager; +import android.os.Binder; +import android.os.Build; +import android.os.UserHandle; +import android.os.UserManager; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; + +/** Unit tests for {@link ConnectivityUtil}. */ +public class ConnectivityUtilTest { + + public static final String TAG = "ConnectivityUtilTest"; + + // Mock objects for testing + @Mock private Context mMockContext; + @Mock private PackageManager mMockPkgMgr; + @Mock private ApplicationInfo mMockApplInfo; + @Mock private AppOpsManager mMockAppOps; + @Mock private UserManager mMockUserManager; + @Mock private LocationManager mLocationManager; + + private static final String TEST_PKG_NAME = "com.google.somePackage"; + private static final String TEST_FEATURE_ID = "com.google.someFeature"; + private static final int MANAGED_PROFILE_UID = 1100000; + private static final int OTHER_USER_UID = 1200000; + + private final String mInteractAcrossUsersFullPermission = + "android.permission.INTERACT_ACROSS_USERS_FULL"; + private final String mManifestStringCoarse = + Manifest.permission.ACCESS_COARSE_LOCATION; + private final String mManifestStringFine = + Manifest.permission.ACCESS_FINE_LOCATION; + + // Test variables + private int mWifiScanAllowApps; + private int mUid; + private int mCoarseLocationPermission; + private int mAllowCoarseLocationApps; + private int mFineLocationPermission; + private int mAllowFineLocationApps; + private int mCurrentUser; + private boolean mIsLocationEnabled; + private boolean mThrowSecurityException; + private Answer<Integer> mReturnPermission; + private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>(); + + private class TestConnectivityUtil extends ConnectivityUtil { + + TestConnectivityUtil(Context context) { + super(context); + } + + @Override + protected int getCurrentUser() { + return mCurrentUser; + } + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + initTestVars(); + } + + private void setupMocks() throws Exception { + when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any())) + .thenReturn(mMockApplInfo); + when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr); + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME, + TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps); + when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid), + eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class))) + .thenReturn(mAllowCoarseLocationApps); + when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid), + eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class))) + .thenReturn(mAllowFineLocationApps); + if (mThrowSecurityException) { + doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong" + + " to application bound to user " + mUid)) + .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME); + } + when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)) + .thenReturn(mMockAppOps); + when(mMockContext.getSystemService(Context.USER_SERVICE)) + .thenReturn(mMockUserManager); + when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager); + } + + private void setupTestCase() throws Exception { + setupMocks(); + setupMockInterface(); + } + + private void initTestVars() { + mPermissionsList.clear(); + mReturnPermission = createPermissionAnswer(); + mWifiScanAllowApps = AppOpsManager.MODE_ERRORED; + mUid = OTHER_USER_UID; + mThrowSecurityException = true; + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M; + mIsLocationEnabled = false; + mCurrentUser = UserHandle.USER_SYSTEM; + mCoarseLocationPermission = PackageManager.PERMISSION_DENIED; + mFineLocationPermission = PackageManager.PERMISSION_DENIED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED; + mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + } + + private void setupMockInterface() { + Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid()); + doAnswer(mReturnPermission).when(mMockContext).checkPermission( + anyString(), anyInt(), anyInt()); + when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM, + UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID))) + .thenReturn(true); + when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid)) + .thenReturn(mCoarseLocationPermission); + when(mMockContext.checkPermission(mManifestStringFine, -1, mUid)) + .thenReturn(mFineLocationPermission); + when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled); + } + + private Answer<Integer> createPermissionAnswer() { + return new Answer<Integer>() { + @Override + public Integer answer(InvocationOnMock invocation) { + int myUid = (int) invocation.getArguments()[1]; + String myPermission = (String) invocation.getArguments()[0]; + mPermissionsList.get(myPermission); + if (mPermissionsList.containsKey(myPermission)) { + int uid = mPermissionsList.get(myPermission); + if (myUid == uid) { + return PackageManager.PERMISSION_GRANTED; + } + } + return PackageManager.PERMISSION_DENIED; + } + }; + } + + @Test + public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception { + mIsLocationEnabled = true; + mThrowSecurityException = false; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + mUid = mCurrentUser; + setupTestCase(); + new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + } + + @Test + public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception { + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q; + mIsLocationEnabled = true; + mThrowSecurityException = false; + mUid = mCurrentUser; + mFineLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + setupTestCase(); + new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + } + + @Test + public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception { + mThrowSecurityException = true; + mIsLocationEnabled = true; + mFineLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + @Test + public void testenforceCanAccessScanResults_UserOrProfileNotCurrent() throws Exception { + mIsLocationEnabled = true; + mThrowSecurityException = false; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + @Test + public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception { + mThrowSecurityException = false; + mIsLocationEnabled = true; + setupTestCase(); + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + @Test + public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception { + mThrowSecurityException = false; + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q; + mIsLocationEnabled = true; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + mUid = MANAGED_PROFILE_UID; + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString()); + } + + @Test + public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception { + mThrowSecurityException = false; + mUid = MANAGED_PROFILE_UID; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid); + mIsLocationEnabled = false; + + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { + try { + r.run(); + Assert.fail("Expected " + exceptionClass + " to be thrown."); + } catch (Exception exception) { + assertTrue(exceptionClass.isInstance(exception)); + } + } +} diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 87657191e901..3f2f3495ae8f 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -44,6 +44,7 @@ cc_library { "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", + "DynamicLibManager.cpp", "Idmap.cpp", "LoadedArsc.cpp", "Locale.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index ca4143f3e215..8cfd2d8ca696 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -25,6 +25,7 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" @@ -66,7 +67,12 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2() { +AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) { + memset(&configuration_, 0, sizeof(configuration_)); +} + +AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager) + : dynamic_lib_manager_(dynamic_lib_manager) { memset(&configuration_, 0, sizeof(configuration_)); } @@ -85,24 +91,44 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); - // A mapping from apk assets path to the runtime package id of its first loaded package. + // Overlay resources are not directly referenced by an application so their resource ids + // can change throughout the application's lifetime. Assign overlay package ids last. + std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_); + std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) { + return !a->IsOverlay(); + }); + std::unordered_map<std::string, uint8_t> apk_assets_package_ids; + std::unordered_map<std::string, uint8_t> package_name_package_ids; + + // Assign stable package ids to application packages. + uint8_t next_available_package_id = 0U; + for (const auto& apk_assets : sorted_apk_assets) { + for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { + uint8_t package_id = package->GetPackageId(); + if (package->IsOverlay()) { + package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id); + next_available_package_id = package_id + 1; + } else if (package->IsDynamic()) { + package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName()); + } - // 0x01 is reserved for the android package. - int next_package_id = 0x02; - const size_t apk_assets_count = apk_assets_.size(); - for (size_t i = 0; i < apk_assets_count; i++) { - const ApkAssets* apk_assets = apk_assets_[i]; - const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + // Map the path of the apk assets to the package id of its first loaded package. + apk_assets_package_ids[apk_assets->GetPath()] = package_id; - for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { - // Get the package ID or assign one if a shared library. - int package_id; - if (package->IsDynamic()) { - package_id = next_package_id++; - } else { - package_id = package->GetPackageId(); - } + // Map the package name of the package to the first loaded package with that package id. + package_name_package_ids[package->GetPackageName()] = package_id; + } + } + + const int apk_assets_count = apk_assets_.size(); + for (int i = 0; i < apk_assets_count; i++) { + const auto& apk_assets = apk_assets_[i]; + for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { + const auto package_id_entry = package_name_package_ids.find(package->GetPackageName()); + CHECK(package_id_entry != package_name_package_ids.end()) + << "no package id assgined to package " << package->GetPackageName(); + const uint8_t package_id = package_id_entry->second; // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; @@ -115,7 +141,10 @@ void AssetManager2::BuildDynamicRefTable() { // to take effect. const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath()); - if (target_package_iter != apk_assets_package_ids.end()) { + if (target_package_iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { const uint8_t target_package_id = target_package_iter->second; const uint8_t target_idx = package_ids_[target_package_id]; CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" @@ -123,7 +152,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup& target_package_group = package_groups_[target_idx]; - // Create a special dynamic reference table for the overlay to rewite references to + // Create a special dynamic reference table for the overlay to rewrite references to // overlay resources as references to the target resources they overlay. auto overlay_table = std::make_shared<OverlayDynamicRefTable>( loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); @@ -153,8 +182,6 @@ void AssetManager2::BuildDynamicRefTable() { package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast<uint8_t>(entry.package_id)); } - - apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -567,7 +594,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (resource_resolution_logging_enabled_) { last_resolution_.steps.push_back( Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), - &package_group.packages_[0].loaded_package_->GetPackageName()}); + overlay_result.package_name}); } } } @@ -1279,6 +1306,16 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const return 0; } +DynamicLibManager* AssetManager2::GetDynamicLibManager() const { + auto dynamic_lib_manager = + std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_); + if (dynamic_lib_manager) { + return (*dynamic_lib_manager).get(); + } else { + return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_); + } +} + std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp new file mode 100644 index 000000000000..895b7695bf26 --- /dev/null +++ b/libs/androidfw/DynamicLibManager.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "androidfw/DynamicLibManager.h" + +namespace android { + +uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) { + auto lib_entry = shared_lib_package_ids_.find(library_package_name); + if (lib_entry != shared_lib_package_ids_.end()) { + return lib_entry->second; + } + + return shared_lib_package_ids_[library_package_name] = next_package_id_++; +} + +uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) { + return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id; +} + +} // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 00cbbcad56e6..b2cec2a42994 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -27,6 +27,7 @@ #include "androidfw/ApkAssets.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -94,6 +95,7 @@ class AssetManager2 { }; AssetManager2(); + explicit AssetManager2(DynamicLibManager* dynamic_lib_manager); // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets // are not owned by the AssetManager, and must have a longer lifetime. @@ -371,6 +373,8 @@ class AssetManager2 { // Retrieve the assigned package id of the package if loaded into this AssetManager uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + DynamicLibManager* GetDynamicLibManager() const; + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; @@ -389,6 +393,9 @@ class AssetManager2 { // may need to be purged. ResTable_config configuration_; + // Component responsible for assigning package ids to shared libraries. + std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_; + // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h new file mode 100644 index 000000000000..1ff7079573d2 --- /dev/null +++ b/libs/androidfw/include/androidfw/DynamicLibManager.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H +#define ANDROIDFW_DYNAMICLIBMANAGER_H + +#include <string> +#include <unordered_map> + +#include "android-base/macros.h" + +namespace android { + +// Manages assigning resource ids for dynamic resources. +class DynamicLibManager { + public: + DynamicLibManager() = default; + + // Retrieves the assigned package id for the library. + uint8_t GetAssignedId(const std::string& library_package_name); + + // Queries in ascending order for the first available package id that is not currently assigned to + // a library. + uint8_t FindUnassignedId(uint8_t start_package_id); + + private: + DISALLOW_COPY_AND_ASSIGN(DynamicLibManager); + + uint8_t next_package_id_ = 0x02; + std::unordered_map<std::string, uint8_t> shared_lib_package_ids_; +}; + +} // namespace android + +#endif //ANDROIDFW_DYNAMICLIBMANAGER_H diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h index 64924f433245..8891512958f0 100644 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -47,7 +47,8 @@ class Guarded { static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); public: - explicit Guarded() : guarded_() { + template <typename ...Args> + explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) { } template <typename U = T> diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index b3190be8caf1..2f6f3dfcaf1c 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -214,6 +214,25 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } +TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) { + DynamicLibManager lib_manager; + AssetManager2 assetmanager(&lib_manager); + assetmanager.SetApkAssets( + {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()}); + + AssetManager2 assetmanager2(&lib_manager); + assetmanager2.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo"); + ASSERT_NE(0U, res_id); + + uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo"); + ASSERT_NE(0U, res_id_2); + + ASSERT_EQ(res_id, res_id_2); +} + TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 23140724ef64..12681ae221d5 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -746,7 +746,10 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai glyphFunc(buffer.glyphs, buffer.pos); sk_sp<SkTextBlob> textBlob(builder.make()); - mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy); + + apply_looper(&paintCopy, [&](const SkPaint& p) { + mCanvas->drawTextBlob(textBlob, 0, 0, p); + }); drawTextDecorations(x, y, totalAdvance, paintCopy); } @@ -783,8 +786,10 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x(); xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y(); } - - this->asSkCanvas()->drawTextBlob(builder.make(), 0, 0, paintCopy); + auto* finalCanvas = this->asSkCanvas(); + apply_looper(&paintCopy, [&](const SkPaint& p) { + finalCanvas->drawTextBlob(builder.make(), 0, 0, paintCopy); + }); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 0a54aca4970d..e075d806126b 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -25,16 +25,16 @@ namespace test { static const int IDENT_DISPLAYEVENT = 1; static android::DisplayInfo DUMMY_DISPLAY{ - 1080, // w - 1920, // h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - 0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline + 1080, // w + 1920, // h + 320.0, // xdpi + 320.0, // ydpi + 60.0, // fps + 2.0, // density + ui::ROTATION_0, // orientation + false, // secure? + 0, // appVsyncOffset + 0, // presentationDeadline }; DisplayInfo getInternalDisplay() { diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl index bcb2336dbf78..e8abfdca89b7 100644 --- a/media/java/android/media/IMediaRoute2ProviderClient.aidl +++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl @@ -18,7 +18,7 @@ package android.media; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2Info; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.Bundle; /** @@ -26,7 +26,7 @@ import android.os.Bundle; */ oneway interface IMediaRoute2ProviderClient { void updateState(in MediaRoute2ProviderInfo providerInfo, - in List<RouteSessionInfo> sessionInfos); - void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, long requestId); - void notifySessionInfoChanged(in RouteSessionInfo sessionInfo); + in List<RoutingSessionInfo> sessionInfos); + void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, long requestId); + void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo); } diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl index f90c7c59cbeb..bc7ebea2e61f 100644 --- a/media/java/android/media/IMediaRouter2Client.aidl +++ b/media/java/android/media/IMediaRouter2Client.aidl @@ -17,7 +17,7 @@ package android.media; import android.media.MediaRoute2Info; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.Bundle; /** @@ -28,7 +28,7 @@ oneway interface IMediaRouter2Client { void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); void notifyRoutesChanged(in List<MediaRoute2Info> routes); - void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId); - void notifySessionInfoChanged(in RouteSessionInfo sessionInfo); - void notifySessionReleased(in RouteSessionInfo sessionInfo); + void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, int requestId); + void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo); + void notifySessionReleased(in RoutingSessionInfo sessionInfo); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 06abdc18ab41..3cdaa0794b31 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -23,7 +23,7 @@ import android.media.IMediaRouterClient; import android.media.MediaRoute2Info; import android.media.MediaRouterClientState; import android.media.RouteDiscoveryPreference; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; /** * {@hide} @@ -70,5 +70,5 @@ interface IMediaRouterService { void requestUpdateVolume2Manager(IMediaRouter2Manager manager, in MediaRoute2Info route, int direction); - List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager); + List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager); } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 8333b0f96c17..1cd5dfa087fa 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -66,7 +66,7 @@ public abstract class MediaRoute2ProviderService extends Service { private MediaRoute2ProviderInfo mProviderInfo; @GuardedBy("mSessionLock") - private ArrayMap<String, RouteSessionInfo> mSessionInfo = new ArrayMap<>(); + private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>(); public MediaRoute2ProviderService() { mHandler = new Handler(Looper.getMainLooper()); @@ -122,7 +122,7 @@ public abstract class MediaRoute2ProviderService extends Service { * @hide */ @Nullable - public final RouteSessionInfo getSessionInfo(@NonNull String sessionId) { + public final RoutingSessionInfo getSessionInfo(@NonNull String sessionId) { if (TextUtils.isEmpty(sessionId)) { throw new IllegalArgumentException("sessionId must not be empty"); } @@ -132,11 +132,11 @@ public abstract class MediaRoute2ProviderService extends Service { } /** - * Gets the list of {@link RouteSessionInfo session info} that the provider service maintains. + * Gets the list of {@link RoutingSessionInfo session info} that the provider service maintains. * @hide */ @NonNull - public final List<RouteSessionInfo> getAllSessionInfo() { + public final List<RoutingSessionInfo> getAllSessionInfo() { synchronized (mSessionLock) { return new ArrayList<>(mSessionInfo.values()); } @@ -149,10 +149,10 @@ public abstract class MediaRoute2ProviderService extends Service { * session info changes. * * @param sessionInfo new session information - * @see #notifySessionCreated(RouteSessionInfo, long) + * @see #notifySessionCreated(RoutingSessionInfo, long) * @hide */ - public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) { + public final void updateSessionInfo(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); @@ -173,7 +173,7 @@ public abstract class MediaRoute2ProviderService extends Service { * TODO: This method is temporary, only created for tests. Remove when the alternative is ready. * @hide */ - public final void notifySessionInfoChanged(@NonNull RouteSessionInfo sessionInfo) { + public final void notifySessionInfoChanged(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); @@ -201,7 +201,7 @@ public abstract class MediaRoute2ProviderService extends Service { * controlled, pass a {@link Bundle} that contains how to control it. * * @param sessionInfo information of the new session. - * The {@link RouteSessionInfo#getId() id} of the session must be + * The {@link RoutingSessionInfo#getId() id} of the session must be * unique. Pass {@code null} to reject the request or inform clients that * session creation is failed. * @param requestId id of the previous request to create this session @@ -209,7 +209,8 @@ public abstract class MediaRoute2ProviderService extends Service { */ // TODO: fail reason? // TODO: Maybe better to create notifySessionCreationFailed? - public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) { + public final void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, + long requestId) { if (sessionInfo != null) { String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { @@ -237,7 +238,7 @@ public abstract class MediaRoute2ProviderService extends Service { * {@link #onDestroySession} is called if the session is released. * * @param sessionId id of the session to be released - * @see #onDestroySession(String, RouteSessionInfo) + * @see #onDestroySession(String, RoutingSessionInfo) * @hide */ public final void releaseSession(@NonNull String sessionId) { @@ -245,7 +246,7 @@ public abstract class MediaRoute2ProviderService extends Service { throw new IllegalArgumentException("sessionId must not be empty"); } //TODO: notify media router service of release. - RouteSessionInfo sessionInfo; + RoutingSessionInfo sessionInfo; synchronized (mSessionLock) { sessionInfo = mSessionInfo.remove(sessionId); } @@ -259,10 +260,10 @@ public abstract class MediaRoute2ProviderService extends Service { /** * Called when a session should be created. * You should create and maintain your own session and notifies the client of - * session info. Call {@link #notifySessionCreated(RouteSessionInfo, long)} + * session info. Call {@link #notifySessionCreated(RoutingSessionInfo, long)} * with the given {@code requestId} to notify the information of a new session. * If you can't create the session or want to reject the request, pass {@code null} - * as session info in {@link #notifySessionCreated(RouteSessionInfo, long)} + * as session info in {@link #notifySessionCreated(RoutingSessionInfo, long)} * with the given {@code requestId}. * * @param packageName the package name of the application that selected the route @@ -285,18 +286,18 @@ public abstract class MediaRoute2ProviderService extends Service { * @hide */ public abstract void onDestroySession(@NonNull String sessionId, - @NonNull RouteSessionInfo lastSessionInfo); + @NonNull RoutingSessionInfo lastSessionInfo); //TODO: make a way to reject the request /** * Called when a client requests selecting a route for the session. - * After the route is selected, call {@link #updateSessionInfo(RouteSessionInfo)} to update + * After the route is selected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * * @param sessionId id of the session * @param routeId id of the route - * @see #updateSessionInfo(RouteSessionInfo) + * @see #updateSessionInfo(RoutingSessionInfo) * @hide */ public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId); @@ -304,7 +305,7 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: make a way to reject the request /** * Called when a client requests deselecting a route from the session. - * After the route is deselected, call {@link #updateSessionInfo(RouteSessionInfo)} to update + * After the route is deselected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * @@ -317,7 +318,7 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: make a way to reject the request /** * Called when a client requests transferring a session to a route. - * After the transfer is finished, call {@link #updateSessionInfo(RouteSessionInfo)} to update + * After the transfer is finished, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * @@ -377,7 +378,7 @@ public abstract class MediaRoute2ProviderService extends Service { return; } - List<RouteSessionInfo> sessionInfos; + List<RoutingSessionInfo> sessionInfos; synchronized (mSessionLock) { sessionInfos = new ArrayList<>(mSessionInfo.values()); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index a89dece79925..6d37c2df63ec 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -84,7 +84,7 @@ public class MediaRouter2 { Client2 mClient; @GuardedBy("sLock") - private Map<String, RouteSessionController> mSessionControllers = new ArrayMap<>(); + private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>(); private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1); @@ -241,7 +241,7 @@ public class MediaRouter2 { } /** - * Registers a callback to get updates on creations and changes of route sessions. + * Registers a callback to get updates on creations and changes of routing sessions. * If you register the same callback twice or more, it will be ignored. * * @param executor the executor to execute the callback on @@ -455,7 +455,7 @@ public class MediaRouter2 { * <p> * Pass {@code null} to sessionInfo for the failure case. */ - void createControllerOnHandler(@Nullable RouteSessionInfo sessionInfo, int requestId) { + void createControllerOnHandler(@Nullable RoutingSessionInfo sessionInfo, int requestId) { SessionCreationRequest matchingRequest = null; for (SessionCreationRequest request : mSessionCreationRequests) { if (request.mRequestId == requestId) { @@ -502,23 +502,23 @@ public class MediaRouter2 { } if (sessionInfo != null) { - RouteSessionController controller = new RouteSessionController(sessionInfo); + RoutingController controller = new RoutingController(sessionInfo); synchronized (sRouterLock) { - mSessionControllers.put(controller.getSessionId(), controller); + mRoutingControllers.put(controller.getSessionId(), controller); } notifySessionCreated(controller); } } - void changeSessionInfoOnHandler(RouteSessionInfo sessionInfo) { + void changeSessionInfoOnHandler(RoutingSessionInfo sessionInfo) { if (sessionInfo == null) { Log.w(TAG, "changeSessionInfoOnHandler: Ignoring null sessionInfo."); return; } - RouteSessionController matchingController; + RoutingController matchingController; synchronized (sRouterLock) { - matchingController = mSessionControllers.get(sessionInfo.getId()); + matchingController = mRoutingControllers.get(sessionInfo.getId()); } if (matchingController == null) { @@ -527,27 +527,27 @@ public class MediaRouter2 { return; } - RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo(); + RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo(); if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) { Log.w(TAG, "changeSessionInfoOnHandler: Provider IDs are not matched. old=" + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId()); return; } - matchingController.setRouteSessionInfo(sessionInfo); + matchingController.setRoutingSessionInfo(sessionInfo); notifySessionInfoChanged(matchingController, oldInfo, sessionInfo); } - void releaseControllerOnHandler(RouteSessionInfo sessionInfo) { + void releaseControllerOnHandler(RoutingSessionInfo sessionInfo) { if (sessionInfo == null) { Log.w(TAG, "releaseControllerOnHandler: Ignoring null sessionInfo."); return; } final String uniqueSessionId = sessionInfo.getId(); - RouteSessionController matchingController; + RoutingController matchingController; synchronized (sRouterLock) { - matchingController = mSessionControllers.get(uniqueSessionId); + matchingController = mRoutingControllers.get(uniqueSessionId); } if (matchingController == null) { @@ -558,7 +558,7 @@ public class MediaRouter2 { return; } - RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo(); + RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo(); if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) { Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old=" + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId()); @@ -566,7 +566,7 @@ public class MediaRouter2 { } synchronized (sRouterLock) { - mSessionControllers.remove(uniqueSessionId, matchingController); + mRoutingControllers.remove(uniqueSessionId, matchingController); } matchingController.release(); notifyControllerReleased(matchingController); @@ -610,7 +610,7 @@ public class MediaRouter2 { } } - private void notifySessionCreated(RouteSessionController controller) { + private void notifySessionCreated(RoutingController controller) { for (SessionCallbackRecord record: mSessionCallbackRecords) { record.mExecutor.execute( () -> record.mSessionCallback.onSessionCreated(controller)); @@ -624,8 +624,8 @@ public class MediaRouter2 { } } - private void notifySessionInfoChanged(RouteSessionController controller, - RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { + private void notifySessionInfoChanged(RoutingController controller, + RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) { for (SessionCallbackRecord record: mSessionCallbackRecords) { record.mExecutor.execute( () -> record.mSessionCallback.onSessionInfoChanged( @@ -633,7 +633,7 @@ public class MediaRouter2 { } } - private void notifyControllerReleased(RouteSessionController controller) { + private void notifyControllerReleased(RoutingController controller) { for (SessionCallbackRecord record: mSessionCallbackRecords) { record.mExecutor.execute( () -> record.mSessionCallback.onSessionReleased(controller)); @@ -678,11 +678,11 @@ public class MediaRouter2 { */ public static class SessionCallback { /** - * Called when the route session is created by the route provider. + * Called when the routing session is created by the route provider. * * @param controller the controller to control the created session */ - public void onSessionCreated(@NonNull RouteSessionController controller) {} + public void onSessionCreated(@NonNull RoutingController controller) {} /** * Called when the session creation request failed. @@ -702,45 +702,45 @@ public class MediaRouter2 { * TODO: (Discussion) Do we really need newInfo? The controller has the newInfo. * However. there can be timing issue if there is no newInfo. */ - public void onSessionInfoChanged(@NonNull RouteSessionController controller, - @NonNull RouteSessionInfo oldInfo, - @NonNull RouteSessionInfo newInfo) {} + public void onSessionInfoChanged(@NonNull RoutingController controller, + @NonNull RoutingSessionInfo oldInfo, + @NonNull RoutingSessionInfo newInfo) {} /** * Called when the session is released by {@link MediaRoute2ProviderService}. * Before this method is called, the controller would be released by the system, - * which means the {@link RouteSessionController#isReleased()} will always return true + * which means the {@link RoutingController#isReleased()} will always return true * for the {@code controller} here. * <p> - * Note: Calling {@link RouteSessionController#release()} will <em>NOT</em> trigger + * Note: Calling {@link RoutingController#release()} will <em>NOT</em> trigger * this method to be called. * * TODO: Add tests for checking whether this method is called. * TODO: When service process dies, this should be called. * - * @see RouteSessionController#isReleased() + * @see RoutingController#isReleased() */ - public void onSessionReleased(@NonNull RouteSessionController controller) {} + public void onSessionReleased(@NonNull RoutingController controller) {} } /** - * A class to control media route session in media route provider. + * A class to control media routing session in media route provider. * For example, selecting/deselcting/transferring routes to session can be done through this * class. Instances are created by {@link MediaRouter2}. * * TODO: Need to add toString() * @hide */ - public final class RouteSessionController { + public final class RoutingController { private final Object mControllerLock = new Object(); @GuardedBy("mLock") - private RouteSessionInfo mSessionInfo; + private RoutingSessionInfo mSessionInfo; @GuardedBy("mLock") private volatile boolean mIsReleased; - RouteSessionController(@NonNull RouteSessionInfo sessionInfo) { + RoutingController(@NonNull RoutingSessionInfo sessionInfo) { mSessionInfo = sessionInfo; } @@ -764,7 +764,7 @@ public class MediaRouter2 { } /** - * @return the control hints used to control route session if available. + * @return the control hints used to control routing session if available. */ @Nullable public Bundle getControlHints() { @@ -986,7 +986,7 @@ public class MediaRouter2 { Client2 client; synchronized (sRouterLock) { - mSessionControllers.remove(getSessionId(), this); + mRoutingControllers.remove(getSessionId(), this); client = mClient; } if (client != null) { @@ -1003,13 +1003,13 @@ public class MediaRouter2 { * @hide */ @NonNull - public RouteSessionInfo getRouteSessionInfo() { + public RoutingSessionInfo getRoutingSessionInfo() { synchronized (mControllerLock) { return mSessionInfo; } } - void setRouteSessionInfo(@NonNull RouteSessionInfo info) { + void setRoutingSessionInfo(@NonNull RoutingSessionInfo info) { synchronized (mControllerLock) { mSessionInfo = info; } @@ -1125,19 +1125,19 @@ public class MediaRouter2 { } @Override - public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) { + public void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, int requestId) { mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler, MediaRouter2.this, sessionInfo, requestId)); } @Override - public void notifySessionInfoChanged(@Nullable RouteSessionInfo sessionInfo) { + public void notifySessionInfoChanged(@Nullable RoutingSessionInfo sessionInfo) { mHandler.sendMessage(obtainMessage(MediaRouter2::changeSessionInfoOnHandler, MediaRouter2.this, sessionInfo)); } @Override - public void notifySessionReleased(RouteSessionInfo sessionInfo) { + public void notifySessionReleased(RoutingSessionInfo sessionInfo) { mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler, MediaRouter2.this, sessionInfo)); } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index f445cdb465fa..70229335905b 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -176,7 +176,7 @@ public class MediaRouter2Manager { } @NonNull - public List<RouteSessionInfo> getActiveSessions() { + public List<RoutingSessionInfo> getActiveSessions() { Client client; synchronized (sLock) { client = mClient; diff --git a/media/java/android/media/RouteSessionInfo.aidl b/media/java/android/media/RoutingSessionInfo.aidl index fb5d836da98e..7b8e3d912cc1 100644 --- a/media/java/android/media/RouteSessionInfo.aidl +++ b/media/java/android/media/RoutingSessionInfo.aidl @@ -16,4 +16,4 @@ package android.media; -parcelable RouteSessionInfo; +parcelable RoutingSessionInfo; diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index def5197874a5..96acf6cec5b4 100644 --- a/media/java/android/media/RouteSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -30,24 +30,24 @@ import java.util.List; import java.util.Objects; /** - * Describes a route session that is made when a media route is selected. + * Describes a routing session which is created when a media route is selected. * @hide */ -public final class RouteSessionInfo implements Parcelable { +public final class RoutingSessionInfo implements Parcelable { @NonNull - public static final Creator<RouteSessionInfo> CREATOR = - new Creator<RouteSessionInfo>() { + public static final Creator<RoutingSessionInfo> CREATOR = + new Creator<RoutingSessionInfo>() { @Override - public RouteSessionInfo createFromParcel(Parcel in) { - return new RouteSessionInfo(in); + public RoutingSessionInfo createFromParcel(Parcel in) { + return new RoutingSessionInfo(in); } @Override - public RouteSessionInfo[] newArray(int size) { - return new RouteSessionInfo[size]; + public RoutingSessionInfo[] newArray(int size) { + return new RoutingSessionInfo[size]; } }; - public static final String TAG = "RouteSessionInfo"; + private static final String TAG = "RoutingSessionInfo"; final String mId; final String mClientPackageName; @@ -61,7 +61,7 @@ public final class RouteSessionInfo implements Parcelable { @Nullable final Bundle mControlHints; - RouteSessionInfo(@NonNull Builder builder) { + RoutingSessionInfo(@NonNull Builder builder) { Objects.requireNonNull(builder, "builder must not be null."); mId = builder.mId; @@ -82,7 +82,7 @@ public final class RouteSessionInfo implements Parcelable { mControlHints = builder.mControlHints; } - RouteSessionInfo(@NonNull Parcel src) { + RoutingSessionInfo(@NonNull Parcel src) { Objects.requireNonNull(src, "src must not be null."); mId = ensureString(src.readString()); @@ -228,11 +228,11 @@ public final class RouteSessionInfo implements Parcelable { if (this == obj) { return true; } - if (!(obj instanceof RouteSessionInfo)) { + if (!(obj instanceof RoutingSessionInfo)) { return false; } - RouteSessionInfo other = (RouteSessionInfo) obj; + RoutingSessionInfo other = (RoutingSessionInfo) obj; return Objects.equals(mId, other.mId) && Objects.equals(mClientPackageName, other.mClientPackageName) && Objects.equals(mRouteFeature, other.mRouteFeature) @@ -252,7 +252,7 @@ public final class RouteSessionInfo implements Parcelable { @Override public String toString() { StringBuilder result = new StringBuilder() - .append("RouteSessionInfo{ ") + .append("RoutingSessionInfo{ ") .append("sessionId=").append(mId) .append(", routeFeature=").append(mRouteFeature) .append(", selectedRoutes={") @@ -290,7 +290,7 @@ public final class RouteSessionInfo implements Parcelable { } /** - * Builder class for {@link RouteSessionInfo}. + * Builder class for {@link RoutingSessionInfo}. */ public static final class Builder { final String mId; @@ -304,10 +304,10 @@ public final class RouteSessionInfo implements Parcelable { Bundle mControlHints; /** - * Constructor for builder to create {@link RouteSessionInfo}. + * Constructor for builder to create {@link RoutingSessionInfo}. * <p> * In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of - * {@link RouteSessionInfo#getId()} can be different from what was set in + * {@link RoutingSessionInfo#getId()} can be different from what was set in * {@link MediaRoute2ProviderService}. * </p> * @@ -337,12 +337,12 @@ public final class RouteSessionInfo implements Parcelable { } /** - * Constructor for builder to create {@link RouteSessionInfo} with - * existing {@link RouteSessionInfo} instance. + * Constructor for builder to create {@link RoutingSessionInfo} with + * existing {@link RoutingSessionInfo} instance. * * @param sessionInfo the existing instance to copy data from. */ - public Builder(@NonNull RouteSessionInfo sessionInfo) { + public Builder(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); mId = sessionInfo.mId; @@ -514,16 +514,16 @@ public final class RouteSessionInfo implements Parcelable { } /** - * Builds a route session info. + * Builds a routing session info. * * @throws IllegalArgumentException if no selected routes are added. */ @NonNull - public RouteSessionInfo build() { + public RoutingSessionInfo build() { if (mSelectedRoutes.isEmpty()) { throw new IllegalArgumentException("selectedRoutes must not be empty"); } - return new RouteSessionInfo(this); + return new RoutingSessionInfo(this); } } } diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java index 938ffcd5f731..dd4dac223a86 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerManager.java +++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java @@ -44,6 +44,7 @@ import com.android.internal.app.ISoundTriggerService; import com.android.internal.util.Preconditions; import java.util.HashMap; +import java.util.Objects; import java.util.UUID; /** @@ -175,19 +176,40 @@ public final class SoundTriggerManager { * Factory constructor to create a SoundModel instance for use with methods in this * class. */ - public static Model create(UUID modelUuid, UUID vendorUuid, byte[] data) { - return new Model(new SoundTrigger.GenericSoundModel(modelUuid, - vendorUuid, data)); + @NonNull + public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid, + @Nullable byte[] data, int version) { + Objects.requireNonNull(modelUuid); + Objects.requireNonNull(vendorUuid); + return new Model(new SoundTrigger.GenericSoundModel(modelUuid, vendorUuid, data, + version)); } + /** + * Factory constructor to create a SoundModel instance for use with methods in this + * class. + */ + @NonNull + public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid, + @Nullable byte[] data) { + return create(modelUuid, vendorUuid, data, -1); + } + + @NonNull public UUID getModelUuid() { return mGenericSoundModel.uuid; } + @NonNull public UUID getVendorUuid() { return mGenericSoundModel.vendorUuid; } + public int getVersion() { + return mGenericSoundModel.version; + } + + @Nullable public byte[] getModelData() { return mGenericSoundModel.data; } diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index b076bb67738f..b5e9d1b2939f 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -60,6 +60,7 @@ interface ITvInputManager { void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession, int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); + int getClientPid(in String sessionId); void setMainSession(in IBinder sessionToken, int userId); void setSurface(in IBinder sessionToken, in Surface surface, int userId); diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl index f90c50491129..8ccf13ae2d72 100755 --- a/media/java/android/media/tv/ITvInputService.aidl +++ b/media/java/android/media/tv/ITvInputService.aidl @@ -30,8 +30,9 @@ oneway interface ITvInputService { void registerCallback(in ITvInputServiceCallback callback); void unregisterCallback(in ITvInputServiceCallback callback); void createSession(in InputChannel channel, in ITvInputSessionCallback callback, - in String inputId); - void createRecordingSession(in ITvInputSessionCallback callback, in String inputId); + in String inputId, in String sessionId); + void createRecordingSession(in ITvInputSessionCallback callback, in String inputId, + in String sessionId); // For hardware TvInputService void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 854ea43f17c3..630d8191eff0 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -266,6 +266,15 @@ public final class TvInputManager { public static final int INPUT_STATE_DISCONNECTED = 2; /** + * An unknown state of the client pid gets from the TvInputManager. Client gets this value when + * query through {@link getClientPid(String sessionId)} fails. + * + * @hide + */ + @SystemApi + public static final int UNKNOWN_CLIENT_PID = -1; + + /** * Broadcast intent action when the user blocked content ratings change. For use with the * {@link #isRatingBlocked}. */ @@ -1484,6 +1493,21 @@ public final class TvInputManager { } /** + * Get a the client pid when creating the session with the session id provided. + * + * @param sessionId a String of session id that is used to query the client pid. + * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_PID} + * if the call fails. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) + public int getClientPid(@NonNull String sessionId) { + return getClientPidInternal(sessionId); + }; + + /** * Creates a recording {@link Session} for a given TV input. * * <p>The number of sessions that can be created at the same time is limited by the capability @@ -1516,6 +1540,17 @@ public final class TvInputManager { } } + private int getClientPidInternal(String sessionId) { + Preconditions.checkNotNull(sessionId); + int clientPid = UNKNOWN_CLIENT_PID; + try { + clientPid = mService.getClientPid(sessionId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return clientPid; + } + /** * Returns the TvStreamConfig list of the given TV input. * diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 7fbb3376d5fb..629dc7ce7819 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -124,7 +124,7 @@ public abstract class TvInputService extends Service { @Override public void createSession(InputChannel channel, ITvInputSessionCallback cb, - String inputId) { + String inputId, String sessionId) { if (channel == null) { Log.w(TAG, "Creating session without input channel"); } @@ -135,17 +135,20 @@ public abstract class TvInputService extends Service { args.arg1 = channel; args.arg2 = cb; args.arg3 = inputId; + args.arg4 = sessionId; mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget(); } @Override - public void createRecordingSession(ITvInputSessionCallback cb, String inputId) { + public void createRecordingSession(ITvInputSessionCallback cb, String inputId, + String sessionId) { if (cb == null) { return; } SomeArgs args = SomeArgs.obtain(); args.arg1 = cb; args.arg2 = inputId; + args.arg3 = sessionId; mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args) .sendToTarget(); } @@ -208,6 +211,37 @@ public abstract class TvInputService extends Service { } /** + * Returns a concrete implementation of {@link Session}. + * + * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager, + * it needs to override this method to get the sessionId passed. When no overriding, this method + * calls {@link #onCreateSession(String)} defaultly. + * + * @param inputId The ID of the TV input associated with the session. + * @param sessionId the unique sessionId created by TIF when session is created. + */ + @Nullable + public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId) { + return onCreateSession(inputId); + } + + /** + * Returns a concrete implementation of {@link RecordingSession}. + * + * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager, + * it needs to override this method to get the sessionId passed. When no overriding, this method + * calls {@link #onCreateRecordingSession(String)} defaultly. + * + * @param inputId The ID of the TV input associated with the recording session. + * @param sessionId the unique sessionId created by TIF when session is created. + */ + @Nullable + public RecordingSession onCreateRecordingSession( + @NonNull String inputId, @NonNull String sessionId) { + return onCreateRecordingSession(inputId); + } + + /** * Returns a new {@link TvInputInfo} object if this service is responsible for * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of * ignoring all hardware input. @@ -2032,8 +2066,9 @@ public abstract class TvInputService extends Service { InputChannel channel = (InputChannel) args.arg1; ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2; String inputId = (String) args.arg3; + String sessionId = (String) args.arg4; args.recycle(); - Session sessionImpl = onCreateSession(inputId); + Session sessionImpl = onCreateSession(inputId, sessionId); if (sessionImpl == null) { try { // Failed to create a session. @@ -2103,8 +2138,10 @@ public abstract class TvInputService extends Service { SomeArgs args = (SomeArgs) msg.obj; ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1; String inputId = (String) args.arg2; + String sessionId = (String) args.arg3; args.recycle(); - RecordingSession recordingSessionImpl = onCreateRecordingSession(inputId); + RecordingSession recordingSessionImpl = + onCreateRecordingSession(inputId, sessionId); if (recordingSessionImpl == null) { try { // Failed to create a recording session. diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index f181b49239d7..c7cc9e6ddb7f 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.Tuner.LnbCallback; @@ -37,6 +38,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi public class Lnb implements AutoCloseable { /** @hide */ @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V, diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index 944ed8c504c9..c2d6c58c6558 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -1298,37 +1298,30 @@ public final class TunerConstants { /** * Operation succeeded. - * @hide */ public static final int RESULT_SUCCESS = Constants.Result.SUCCESS; /** * Operation failed because the corresponding resources are not available. - * @hide */ public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE; /** * Operation failed because the corresponding resources are not initialized. - * @hide */ public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED; /** * Operation failed because it's not in a valid state. - * @hide */ public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE; /** * Operation failed because there are invalid arguments. - * @hide */ public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT; /** * Memory allocation failed. - * @hide */ public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY; /** * Operation failed due to unknown errors. - * @hide */ public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR; diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index b81c378ad2ba..221783b1c97d 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -22,7 +22,7 @@ import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV; import android.content.Intent; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderService; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.IBinder; import android.text.TextUtils; @@ -63,7 +63,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService "com.android.mediarouteprovider.FEATURE_SPECIAL"; Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); - Map<String, String> mRouteSessionMap = new HashMap<>(); + Map<String, String> mRouteIdToSessionId = new HashMap<>(); private int mNextSessionId = 1000; private void initializeRoutes() { @@ -183,9 +183,9 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService mRoutes.put(routeId, new MediaRoute2Info.Builder(route) .setClientPackageName(packageName) .build()); - mRouteSessionMap.put(routeId, sessionId); + mRouteIdToSessionId.put(routeId, sessionId); - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( sessionId, packageName, routeFeature) .addSelectedRoute(routeId) .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT) @@ -196,9 +196,9 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onDestroySession(String sessionId, RouteSessionInfo lastSessionInfo) { + public void onDestroySession(String sessionId, RoutingSessionInfo lastSessionInfo) { for (String routeId : lastSessionInfo.getSelectedRoutes()) { - mRouteSessionMap.remove(routeId); + mRouteIdToSessionId.remove(routeId); MediaRoute2Info route = mRoutes.get(routeId); if (route != null) { mRoutes.put(routeId, new MediaRoute2Info.Builder(route) @@ -210,7 +210,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onSelectRoute(String sessionId, String routeId) { - RouteSessionInfo sessionInfo = getSessionInfo(sessionId); + RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); if (route == null || sessionInfo == null) { return; @@ -220,9 +220,9 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService mRoutes.put(routeId, new MediaRoute2Info.Builder(route) .setClientPackageName(sessionInfo.getClientPackageName()) .build()); - mRouteSessionMap.put(routeId, sessionId); + mRouteIdToSessionId.put(routeId, sessionId); - RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .addSelectedRoute(routeId) .removeSelectableRoute(routeId) .addDeselectableRoute(routeId) @@ -233,15 +233,15 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onDeselectRoute(String sessionId, String routeId) { - RouteSessionInfo sessionInfo = getSessionInfo(sessionId); + RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); - mRouteSessionMap.remove(routeId); if (sessionInfo == null || route == null || !sessionInfo.getSelectedRoutes().contains(routeId)) { return; } + mRouteIdToSessionId.remove(routeId); mRoutes.put(routeId, new MediaRoute2Info.Builder(route) .setClientPackageName(null) .build()); @@ -251,7 +251,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService return; } - RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .removeSelectedRoute(routeId) .addSelectableRoute(routeId) .removeDeselectableRoute(routeId) @@ -262,8 +262,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onTransferToRoute(String sessionId, String routeId) { - RouteSessionInfo sessionInfo = getSessionInfo(sessionId); - RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .clearSelectedRoutes() .addSelectedRoute(routeId) .removeDeselectableRoute(routeId) @@ -274,11 +274,11 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } void maybeDeselectRoute(String routeId) { - if (!mRouteSessionMap.containsKey(routeId)) { + if (!mRouteIdToSessionId.containsKey(routeId)) { return; } - String sessionId = mRouteSessionMap.get(routeId); + String sessionId = mRouteIdToSessionId.get(routeId); onDeselectRoute(sessionId, routeId); } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java index 908d7bf92c35..007229a4df2b 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java @@ -48,10 +48,10 @@ import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRouter2; import android.media.MediaRouter2.RouteCallback; -import android.media.MediaRouter2.RouteSessionController; +import android.media.MediaRouter2.RoutingController; import android.media.MediaRouter2.SessionCallback; import android.media.RouteDiscoveryPreference; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.net.Uri; import android.os.Parcel; import android.support.test.InstrumentationRegistry; @@ -253,12 +253,12 @@ public class MediaRouter2Test { final CountDownLatch successLatch = new CountDownLatch(1); final CountDownLatch failureLatch = new CountDownLatch(1); - final List<RouteSessionController> controllers = new ArrayList<>(); + final List<RoutingController> controllers = new ArrayList<>(); // Create session with this route SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)); assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); @@ -302,12 +302,12 @@ public class MediaRouter2Test { final CountDownLatch successLatch = new CountDownLatch(1); final CountDownLatch failureLatch = new CountDownLatch(1); - final List<RouteSessionController> controllers = new ArrayList<>(); + final List<RoutingController> controllers = new ArrayList<>(); // Create session with this route SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { controllers.add(controller); successLatch.countDown(); } @@ -346,12 +346,12 @@ public class MediaRouter2Test { final CountDownLatch successLatch = new CountDownLatch(2); final CountDownLatch failureLatch = new CountDownLatch(1); - final List<RouteSessionController> createdControllers = new ArrayList<>(); + final List<RoutingController> createdControllers = new ArrayList<>(); // Create session with this route SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { createdControllers.add(controller); successLatch.countDown(); } @@ -384,8 +384,8 @@ public class MediaRouter2Test { // Created controllers should have proper info assertEquals(2, createdControllers.size()); - RouteSessionController controller1 = createdControllers.get(0); - RouteSessionController controller2 = createdControllers.get(1); + RoutingController controller1 = createdControllers.get(0); + RoutingController controller2 = createdControllers.get(1); assertNotEquals(controller1.getSessionId(), controller2.getSessionId()); assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1)); @@ -411,12 +411,12 @@ public class MediaRouter2Test { final CountDownLatch successLatch = new CountDownLatch(1); final CountDownLatch failureLatch = new CountDownLatch(1); - final List<RouteSessionController> controllers = new ArrayList<>(); + final List<RoutingController> controllers = new ArrayList<>(); // Create session with this route SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { controllers.add(controller); successLatch.countDown(); } @@ -451,7 +451,7 @@ public class MediaRouter2Test { // TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route) @Test - public void testRouteSessionControllerSelectAndDeselectRoute() throws Exception { + public void testRoutingControllerSelectAndDeselectRoute() throws Exception { final List<String> sampleRouteType = new ArrayList<>(); sampleRouteType.add(FEATURE_SAMPLE); @@ -462,12 +462,12 @@ public class MediaRouter2Test { final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1); final CountDownLatch onSessionInfoChangedLatchForSelect = new CountDownLatch(1); final CountDownLatch onSessionInfoChangedLatchForDeselect = new CountDownLatch(1); - final List<RouteSessionController> controllers = new ArrayList<>(); + final List<RoutingController> controllers = new ArrayList<>(); // Create session with ROUTE_ID1 SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); @@ -476,8 +476,8 @@ public class MediaRouter2Test { } @Override - public void onSessionInfoChanged(RouteSessionController controller, - RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { + public void onSessionInfoChanged(RoutingController controller, + RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) { if (onSessionCreatedLatch.getCount() != 0 || !TextUtils.equals( controllers.get(0).getSessionId(), controller.getSessionId())) { @@ -527,7 +527,7 @@ public class MediaRouter2Test { assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); - RouteSessionController controller = controllers.get(0); + RoutingController controller = controllers.get(0); assertTrue(getRouteIds(controller.getSelectableRoutes()) .contains(ROUTE_ID4_TO_SELECT_AND_DESELECT)); @@ -550,7 +550,7 @@ public class MediaRouter2Test { } @Test - public void testRouteSessionControllerTransferToRoute() throws Exception { + public void testRoutingControllerTransferToRoute() throws Exception { final List<String> sampleRouteType = new ArrayList<>(); sampleRouteType.add(FEATURE_SAMPLE); @@ -560,12 +560,12 @@ public class MediaRouter2Test { final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1); final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1); - final List<RouteSessionController> controllers = new ArrayList<>(); + final List<RoutingController> controllers = new ArrayList<>(); // Create session with ROUTE_ID1 SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); @@ -574,8 +574,8 @@ public class MediaRouter2Test { } @Override - public void onSessionInfoChanged(RouteSessionController controller, - RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { + public void onSessionInfoChanged(RoutingController controller, + RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) { if (onSessionCreatedLatch.getCount() != 0 || !TextUtils.equals( controllers.get(0).getSessionId(), controller.getSessionId())) { @@ -609,7 +609,7 @@ public class MediaRouter2Test { assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); - RouteSessionController controller = controllers.get(0); + RoutingController controller = controllers.get(0); assertTrue(getRouteIds(controller.getTransferrableRoutes()) .contains(ROUTE_ID5_TO_TRANSFER_TO)); @@ -629,7 +629,7 @@ public class MediaRouter2Test { // TODO: Add tests for onSessionReleased() call. @Test - public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception { + public void testRoutingControllerReleaseShouldIgnoreTransferTo() throws Exception { final List<String> sampleRouteType = new ArrayList<>(); sampleRouteType.add(FEATURE_SAMPLE); @@ -639,12 +639,12 @@ public class MediaRouter2Test { final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1); final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1); - final List<RouteSessionController> controllers = new ArrayList<>(); + final List<RoutingController> controllers = new ArrayList<>(); // Create session with ROUTE_ID1 SessionCallback sessionCallback = new SessionCallback() { @Override - public void onSessionCreated(RouteSessionController controller) { + public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); @@ -653,8 +653,8 @@ public class MediaRouter2Test { } @Override - public void onSessionInfoChanged(RouteSessionController controller, - RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { + public void onSessionInfoChanged(RoutingController controller, + RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) { if (onSessionCreatedLatch.getCount() != 0 || !TextUtils.equals( controllers.get(0).getSessionId(), controller.getSessionId())) { @@ -674,7 +674,7 @@ public class MediaRouter2Test { assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); - RouteSessionController controller = controllers.get(0); + RoutingController controller = controllers.get(0); assertTrue(getRouteIds(controller.getTransferrableRoutes()) .contains(ROUTE_ID5_TO_TRANSFER_TO)); @@ -732,8 +732,8 @@ public class MediaRouter2Test { } } - static void releaseControllers(@NonNull List<RouteSessionController> controllers) { - for (RouteSessionController controller : controllers) { + static void releaseControllers(@NonNull List<RoutingController> controllers) { + for (RoutingController controller : controllers) { controller.release(); } } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 5dabb7dd818d..ae15b913631b 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -228,7 +228,7 @@ public class MediaRouterManagerTest { addRouterCallback(new MediaRouter2.RouteCallback()); addSessionCallback(new SessionCallback() { @Override - public void onSessionCreated(MediaRouter2.RouteSessionController controller) { + public void onSessionCreated(MediaRouter2.RoutingController controller) { if (createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) { latch.countDown(); } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java index 6d21b3326521..3f5973615e32 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionInfoTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertThrows; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.Bundle; import android.os.Parcel; import android.support.test.filters.SmallTest; @@ -32,11 +32,11 @@ import org.junit.Test; import org.junit.runner.RunWith; /** - * Tests {@link RouteSessionInfo} and its {@link RouteSessionInfo.Builder builder}. + * Tests {@link RoutingSessionInfo} and its {@link RoutingSessionInfo.Builder builder}. */ @RunWith(AndroidJUnit4.class) @SmallTest -public class RouteSessionInfoTest { +public class RoutingSessionInfoTest { public static final String TEST_ID = "test_id"; public static final String TEST_CLIENT_PACKAGE_NAME = "com.test.client.package.name"; public static final String TEST_ROUTE_FEATURE = "test_route_feature"; @@ -68,71 +68,71 @@ public class RouteSessionInfoTest { final String validRouteFeature = TEST_ROUTE_FEATURE; // ID is invalid - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( nullId, validClientPackageName, validRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( emptyId, validClientPackageName, validRouteFeature)); // client package name is invalid (null) - assertThrows(NullPointerException.class, () -> new RouteSessionInfo.Builder( + assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder( validId, nullClientPackageName, validRouteFeature)); // route feature is invalid - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( validId, validClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( validId, validClientPackageName, emptyRouteFeature)); // Two arguments are invalid - (1) ID and clientPackageName - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( nullId, nullClientPackageName, validRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( emptyId, nullClientPackageName, validRouteFeature)); // Two arguments are invalid - (2) ID and routeFeature - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( nullId, validClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( nullId, validClientPackageName, emptyRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( emptyId, validClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( emptyId, validClientPackageName, emptyRouteFeature)); // Two arguments are invalid - (3) clientPackageName and routeFeature // Note that this throws NullPointerException. - assertThrows(NullPointerException.class, () -> new RouteSessionInfo.Builder( + assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder( validId, nullClientPackageName, nullRouteFeature)); - assertThrows(NullPointerException.class, () -> new RouteSessionInfo.Builder( + assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder( validId, nullClientPackageName, emptyRouteFeature)); // All arguments are invalid - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( nullId, nullClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( nullId, nullClientPackageName, emptyRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( emptyId, nullClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder( + assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( emptyId, nullClientPackageName, emptyRouteFeature)); // Null RouteInfo (1-argument constructor) - final RouteSessionInfo nullRouteSessionInfo = null; + final RoutingSessionInfo nullRoutingSessionInfo = null; assertThrows(NullPointerException.class, - () -> new RouteSessionInfo.Builder(nullRouteSessionInfo)); + () -> new RoutingSessionInfo.Builder(nullRoutingSessionInfo)); } @Test public void testBuilderConstructorWithEmptyClientPackageName() { // An empty string for client package name is valid. (for unknown cases) // Creating builder with it should not throw any exception. - RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder( + RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( TEST_ID, "" /* clientPackageName*/, TEST_ROUTE_FEATURE); } @Test public void testBuilderBuildWithEmptySelectedRoutesThrowsIAE() { - RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder( + RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE); // Note: Calling build() without adding any selected routes. assertThrows(IllegalArgumentException.class, () -> builder.build()); @@ -140,7 +140,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderAddRouteMethodsWithIllegalArgumentsThrowsIAE() { - RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder( + RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE); final String nullRouteId = null; @@ -167,7 +167,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderRemoveRouteMethodsWithIllegalArgumentsThrowsIAE() { - RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder( + RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE); final String nullRouteId = null; @@ -193,11 +193,11 @@ public class RouteSessionInfoTest { } @Test - public void testBuilderAndGettersOfRouteSessionInfo() { + public void testBuilderAndGettersOfRoutingSessionInfo() { Bundle controlHints = new Bundle(); controlHints.putString(TEST_KEY, TEST_VALUE); - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -238,7 +238,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderAddRouteMethodsWithBuilderCopyConstructor() { - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -246,7 +246,7 @@ public class RouteSessionInfoTest { .addTransferrableRoute(TEST_ROUTE_ID_6) .build(); - RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_3) .addDeselectableRoute(TEST_ROUTE_ID_5) @@ -272,7 +272,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderRemoveRouteMethods() { - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -307,7 +307,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderRemoveRouteMethodsWithBuilderCopyConstructor() { - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -319,7 +319,7 @@ public class RouteSessionInfoTest { .addTransferrableRoute(TEST_ROUTE_ID_7) .build(); - RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .removeSelectedRoute(TEST_ROUTE_ID_1) .removeSelectableRoute(TEST_ROUTE_ID_3) .removeDeselectableRoute(TEST_ROUTE_ID_5) @@ -341,7 +341,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderClearRouteMethods() { - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -373,7 +373,7 @@ public class RouteSessionInfoTest { @Test public void testBuilderClearRouteMethodsWithBuilderCopyConstructor() { - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -385,7 +385,7 @@ public class RouteSessionInfoTest { .addTransferrableRoute(TEST_ROUTE_ID_7) .build(); - RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .clearSelectedRoutes() .clearSelectableRoutes() .clearDeselectableRoutes() @@ -407,7 +407,7 @@ public class RouteSessionInfoTest { Bundle controlHints = new Bundle(); controlHints.putString(TEST_KEY, TEST_VALUE); - RouteSessionInfo sessionInfo1 = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -420,7 +420,7 @@ public class RouteSessionInfoTest { .setControlHints(controlHints) .build(); - RouteSessionInfo sessionInfo2 = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -442,7 +442,7 @@ public class RouteSessionInfoTest { Bundle controlHints = new Bundle(); controlHints.putString(TEST_KEY, TEST_VALUE); - RouteSessionInfo sessionInfo1 = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -455,7 +455,7 @@ public class RouteSessionInfoTest { .setControlHints(controlHints) .build(); - RouteSessionInfo sessionInfo2 = new RouteSessionInfo.Builder(sessionInfo1).build(); + RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(sessionInfo1).build(); assertEquals(sessionInfo1, sessionInfo2); assertEquals(sessionInfo1.hashCode(), sessionInfo2.hashCode()); @@ -466,7 +466,7 @@ public class RouteSessionInfoTest { Bundle controlHints = new Bundle(); controlHints.putString(TEST_KEY, TEST_VALUE); - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -480,44 +480,44 @@ public class RouteSessionInfoTest { .build(); // Now, we will use copy constructor - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .addSelectedRoute("randomRoute") .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .addSelectableRoute("randomRoute") .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .addDeselectableRoute("randomRoute") .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .addTransferrableRoute("randomRoute") .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .removeSelectedRoute(TEST_ROUTE_ID_1) .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .removeSelectableRoute(TEST_ROUTE_ID_3) .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .removeDeselectableRoute(TEST_ROUTE_ID_5) .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .removeTransferrableRoute(TEST_ROUTE_ID_7) .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .clearSelectedRoutes() // Note: Calling build() with empty selected routes will throw IAE. .addSelectedRoute(TEST_ROUTE_ID_0) .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .clearSelectableRoutes() .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .clearDeselectableRoutes() .build()); - assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo) + assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo) .clearTransferrableRoutes() .build()); @@ -529,7 +529,7 @@ public class RouteSessionInfoTest { Bundle controlHints = new Bundle(); controlHints.putString(TEST_KEY, TEST_VALUE); - RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) @@ -546,7 +546,8 @@ public class RouteSessionInfoTest { sessionInfo.writeToParcel(parcel, 0); parcel.setDataPosition(0); - RouteSessionInfo sessionInfoFromParcel = RouteSessionInfo.CREATOR.createFromParcel(parcel); + RoutingSessionInfo sessionInfoFromParcel = + RoutingSessionInfo.CREATOR.createFromParcel(parcel); assertEquals(sessionInfo, sessionInfoFromParcel); assertEquals(sessionInfo.hashCode(), sessionInfoFromParcel.hashCode()); diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java index 65542673a607..cf55eba6e5ba 100644 --- a/mms/java/android/telephony/MmsManager.java +++ b/mms/java/android/telephony/MmsManager.java @@ -16,8 +16,12 @@ package android.telephony; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemService; import android.app.ActivityThread; import android.app.PendingIntent; +import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; @@ -27,22 +31,18 @@ import com.android.internal.telephony.IMms; /** * Manages MMS operations such as sending multimedia messages. - * Get this object by calling the static method {@link #getInstance()}. - * @hide + * Get this object by calling Context#getSystemService(Context#MMS_SERVICE). */ +@SystemService(Context.MMS_SERVICE) public class MmsManager { private static final String TAG = "MmsManager"; - - /** Singleton object constructed during class initialization. */ - private static final MmsManager sInstance = new MmsManager(); + private final Context mContext; /** - * Get the MmsManager singleton instance. - * - * @return the {@link MmsManager} singleton instance. + * @hide */ - public static MmsManager getInstance() { - return sInstance; + public MmsManager(@NonNull Context context) { + mContext = context; } /** @@ -56,8 +56,9 @@ public class MmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message * is successfully sent, or failed */ - public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl, - Bundle configOverrides, PendingIntent sentIntent) { + public void sendMultimediaMessage(int subId, @NonNull Uri contentUri, + @Nullable String locationUrl, @Nullable Bundle configOverrides, + @Nullable PendingIntent sentIntent) { try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { @@ -84,8 +85,9 @@ public class MmsManager { * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty */ - public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri, - Bundle configOverrides, PendingIntent downloadedIntent) { + public void downloadMultimediaMessage(int subId, @NonNull String locationUrl, + @NonNull Uri contentUri, @Nullable Bundle configOverrides, + @Nullable PendingIntent downloadedIntent) { try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { diff --git a/packages/CarSystemUI/res/layout/sysui_primary_window.xml b/packages/CarSystemUI/res/layout/sysui_primary_window.xml new file mode 100644 index 000000000000..309d9ef3ccfd --- /dev/null +++ b/packages/CarSystemUI/res/layout/sysui_primary_window.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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:background="@android:color/transparent" + android:layout_width="match_parent" + android:layout_height="match_parent"> +</FrameLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java new file mode 100644 index 000000000000..e3e9ab75e3a2 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.car; + +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.os.Binder; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.policy.ConfigurationController; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Controls the expansion state of the primary window which will contain all of the fullscreen sysui + * behavior. This window still has a collapsed state in order to watch for swipe events to expand + * this window for the notification panel. + */ +@Singleton +public class SystemUIPrimaryWindowController implements + ConfigurationController.ConfigurationListener { + + private final Context mContext; + private final Resources mResources; + private final WindowManager mWindowManager; + + private final int mStatusBarHeight; + private final int mNavBarHeight; + private final int mDisplayHeight; + private ViewGroup mBaseLayout; + private WindowManager.LayoutParams mLp; + private WindowManager.LayoutParams mLpChanged; + + @Inject + public SystemUIPrimaryWindowController( + Context context, + @Main Resources resources, + WindowManager windowManager, + ConfigurationController configurationController + ) { + mContext = context; + mResources = resources; + mWindowManager = windowManager; + + Point display = new Point(); + mWindowManager.getDefaultDisplay().getSize(display); + mDisplayHeight = display.y; + + mStatusBarHeight = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.navigation_bar_height); + + mLpChanged = new WindowManager.LayoutParams(); + mBaseLayout = (ViewGroup) LayoutInflater.from(context) + .inflate(R.layout.sysui_primary_window, /* root= */ null, false); + + configurationController.addCallback(this); + } + + /** Returns the base view of the primary window. */ + public ViewGroup getBaseLayout() { + return mBaseLayout; + } + + /** Attaches the window to the window manager. */ + public void attach() { + // Now that the status bar window encompasses the sliding panel and its + // translucent backdrop, the entire thing is made TRANSLUCENT and is + // hardware-accelerated. + mLp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + mStatusBarHeight, + WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + PixelFormat.TRANSLUCENT); + mLp.token = new Binder(); + mLp.gravity = Gravity.TOP; + mLp.setFitWindowInsetsTypes(/* types= */ 0); + mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + mLp.setTitle("NotificationShade"); + mLp.packageName = mContext.getPackageName(); + mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + + mWindowManager.addView(mBaseLayout, mLp); + mLpChanged.copyFrom(mLp); + } + + /** Sets the window to the expanded state. */ + public void setWindowExpanded(boolean expanded) { + if (expanded) { + // TODO: Update this so that the windowing type gets the full height of the display + // when we use MATCH_PARENT. + mLpChanged.height = mDisplayHeight + mNavBarHeight; + } else { + mLpChanged.height = mStatusBarHeight; + } + updateWindow(); + } + + /** Returns {@code true} if the window is expanded */ + public boolean isWindowExpanded() { + return mLp.height != mStatusBarHeight; + } + + private void updateWindow() { + if (mLp != null && mLp.copyFrom(mLpChanged) != 0) { + mWindowManager.updateViewLayout(mBaseLayout, mLp); + } + } +} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index 642dc82c4d28..41a9b24afa03 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -36,6 +36,7 @@ import android.net.http.SslError; import android.os.Bundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -49,7 +50,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.ArrayUtils; import com.android.internal.util.TrafficStatsConstants; @@ -203,7 +203,7 @@ public class CaptivePortalLoginActivity extends Activity { } private URL getUrlForCaptivePortal() { - String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY); + String url = getIntent().getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL); if (TextUtils.isEmpty(url)) url = mCm.getCaptivePortalServerUrl(); final CarrierConfigManager configManager = getApplicationContext() .getSystemService(CarrierConfigManager.class); diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java index 46b1d5fe27be..c7f5e9a5ceec 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java @@ -19,10 +19,10 @@ import android.content.Context; import android.content.Intent; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; -import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; @@ -50,7 +50,7 @@ public class CustomConfigLoader { * @param intent passing signal for config match * @return a list of carrier action for the given signal based on the carrier config. * - * Example: input intent TelephonyIntent.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED + * Example: input intent TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED * This intent allows fined-grained matching based on both intent type & extra values: * apnType and errorCode. * apnType read from passing intent is "default" and errorCode is 0x26 for example and @@ -78,25 +78,25 @@ public class CustomConfigLoader { String arg1 = null; String arg2 = null; switch (intent.getAction()) { - case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED: + case TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED: configs = b.getStringArray(CarrierConfigManager .KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY); break; - case TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED: + case TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED: configs = b.getStringArray(CarrierConfigManager .KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY); - arg1 = intent.getStringExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY); - arg2 = intent.getStringExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY); + arg1 = intent.getStringExtra(TelephonyManager.EXTRA_APN_TYPE); + arg2 = intent.getStringExtra(TelephonyManager.EXTRA_ERROR_CODE); break; - case TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET: + case TelephonyManager.ACTION_CARRIER_SIGNAL_RESET: configs = b.getStringArray(CarrierConfigManager .KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET); break; - case TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE: + case TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE: configs = b.getStringArray(CarrierConfigManager .KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE); - arg1 = String.valueOf(intent.getBooleanExtra(TelephonyIntents - .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false)); + arg1 = String.valueOf(intent.getBooleanExtra(TelephonyManager + .EXTRA_DEFAULT_NETWORK_AVAILABLE, false)); break; default: Log.e(TAG, "load carrier config failure with un-configured key: " diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java index 3e34f0aa6124..78a02d71fc9f 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java @@ -27,10 +27,9 @@ import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.provider.Settings; +import android.telephony.TelephonyManager; import android.util.Log; -import com.android.internal.telephony.TelephonyIntents; - /** * Service to run {@link android.app.job.JobScheduler} job. * Service to monitor when there is a change to conent URI @@ -93,7 +92,7 @@ public class ProvisionObserver extends JobService { } int jobId; switch(intent.getAction()) { - case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED: + case TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED: jobId = PROVISION_OBSERVER_REEVALUATION_JOB_ID; break; default: diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java index 1928ad9add3e..086a287fd243 100644 --- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java +++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java @@ -15,6 +15,11 @@ */ package com.android.carrierdefaultapp; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -25,7 +30,6 @@ import android.telephony.TelephonyManager; import android.test.InstrumentationTestCase; import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.TelephonyIntents; import org.junit.After; import org.junit.Before; @@ -34,10 +38,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; public class CarrierDefaultReceiverTest extends InstrumentationTestCase { @Mock @@ -87,7 +87,7 @@ public class CarrierDefaultReceiverTest extends InstrumentationTestCase { .KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY, new String[]{"4,1"}); doReturn(b).when(mCarrierConfigMgr).getConfig(); - Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED); + Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); mReceiver.onReceive(mContext, intent); diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java index 69bd0ed0c59c..ff00fb3282b1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java @@ -16,8 +16,6 @@ package com.android.settingslib; -import static android.content.Context.TELEPHONY_SERVICE; - import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -172,36 +170,38 @@ public class DeviceInfoUtils { } } - public static String getFormattedPhoneNumber(Context context, SubscriptionInfo subscriptionInfo) { + /** + * Format a phone number. + * @param subscriptionInfo {@link SubscriptionInfo} subscription information. + * @return Returns formatted phone number. + */ + public static String getFormattedPhoneNumber(Context context, + SubscriptionInfo subscriptionInfo) { String formattedNumber = null; if (subscriptionInfo != null) { - final TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE); - final String rawNumber = - telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId()); + final TelephonyManager telephonyManager = context.getSystemService( + TelephonyManager.class); + final String rawNumber = telephonyManager.createForSubscriptionId( + subscriptionInfo.getSubscriptionId()).getLine1Number(); if (!TextUtils.isEmpty(rawNumber)) { formattedNumber = PhoneNumberUtils.formatNumber(rawNumber); } - } return formattedNumber; } public static String getFormattedPhoneNumbers(Context context, - List<SubscriptionInfo> subscriptionInfo) { + List<SubscriptionInfo> subscriptionInfoList) { StringBuilder sb = new StringBuilder(); - if (subscriptionInfo != null) { - final TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE); - final int count = subscriptionInfo.size(); - for (int i = 0; i < count; i++) { - final String rawNumber = telephonyManager.getLine1Number( - subscriptionInfo.get(i).getSubscriptionId()); + if (subscriptionInfoList != null) { + final TelephonyManager telephonyManager = context.getSystemService( + TelephonyManager.class); + final int count = subscriptionInfoList.size(); + for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) { + final String rawNumber = telephonyManager.createForSubscriptionId( + subscriptionInfo.getSubscriptionId()).getLine1Number(); if (!TextUtils.isEmpty(rawNumber)) { - sb.append(PhoneNumberUtils.formatNumber(rawNumber)); - if (i < count - 1) { - sb.append("\n"); - } + sb.append(PhoneNumberUtils.formatNumber(rawNumber)).append("\n"); } } } diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_item.xml b/packages/SystemUI/res/layout-land/global_actions_grid_item.xml index bc1233828b4d..0f9deaa3c569 100644 --- a/packages/SystemUI/res/layout-land/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout-land/global_actions_grid_item.xml @@ -57,15 +57,5 @@ android:textColor="@color/global_actions_text" android:textAppearance="?android:attr/textAppearanceSmall" /> - - <TextView - android:visibility="gone" - android:id="@*android:id/status" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textColor="@color/global_actions_text" - android:textAppearance="?android:attr/textAppearanceSmall" - /> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml index 4404c87432fe..31c7cbf6ff1b 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -56,15 +56,5 @@ android:textColor="@color/global_actions_text" android:textAppearance="?android:attr/textAppearanceSmall" /> - - <TextView - android:visibility="gone" - android:id="@*android:id/status" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textColor="@color/global_actions_text" - android:textAppearance="?android:attr/textAppearanceSmall" - /> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml index 6ac9da4291c6..995cb7d48e60 100644 --- a/packages/SystemUI/res/layout/global_screenshot.xml +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- Copyright (C) 2020 The Android Open 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/packages/SystemUI/res/layout/global_screenshot_legacy.xml b/packages/SystemUI/res/layout/global_screenshot_legacy.xml new file mode 100644 index 000000000000..791c7ea875fe --- /dev/null +++ b/packages/SystemUI/res/layout/global_screenshot_legacy.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView android:id="@+id/global_screenshot_legacy_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@android:color/black" + android:visibility="gone" /> + <ImageView android:id="@+id/global_screenshot_legacy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:background="@drawable/screenshot_panel" + android:visibility="gone" + android:adjustViewBounds="true" /> + <ImageView android:id="@+id/global_screenshot_legacy_flash" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@android:color/white" + android:visibility="gone" /> + <com.android.systemui.screenshot.ScreenshotSelectorView + android:id="@+id/global_screenshot_legacy_selector" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + android:pointerIcon="crosshair"/> +</FrameLayout> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index da0323ac7357..5a1151eb9f6e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -288,6 +288,7 @@ <!-- Dimensions related to screenshots --> <!-- The padding on the global screenshot background image --> + <dimen name="global_screenshot_legacy_bg_padding">20dp</dimen> <dimen name="global_screenshot_bg_padding">20dp</dimen> <dimen name="screenshot_action_container_corner_radius">10dp</dimen> <dimen name="screenshot_action_container_padding">20dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index b0831232bcd4..23d6458a30a5 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -18,7 +18,6 @@ package com.android.systemui.appops; import android.app.AppOpsManager; import android.content.Context; -import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -64,7 +63,6 @@ public class AppOpsControllerImpl implements AppOpsController, private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); - private final PermissionFlagsCache mFlagsCache; private boolean mListening; @GuardedBy("mActiveItems") @@ -81,17 +79,10 @@ public class AppOpsControllerImpl implements AppOpsController, }; @Inject - public AppOpsControllerImpl(Context context, @Background Looper bgLooper, - DumpController dumpController) { - this(context, bgLooper, new PermissionFlagsCache(context), dumpController); - } - - @VisibleForTesting - protected AppOpsControllerImpl(Context context, Looper bgLooper, PermissionFlagsCache cache, - DumpController dumpController) { + public AppOpsControllerImpl(Context context, + @Background Looper bgLooper, DumpController dumpController) { mContext = context; mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mFlagsCache = cache; mBGHandler = new H(bgLooper); final int numOps = OPS.length; for (int i = 0; i < numOps; i++) { @@ -209,7 +200,14 @@ public class AppOpsControllerImpl implements AppOpsController, mNotedItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); } - notifySuscribers(code, uid, packageName, false); + boolean active; + // Check if the item is also active + synchronized (mActiveItems) { + active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; + } + if (!active) { + notifySuscribers(code, uid, packageName, false); + } } private boolean addNoted(int code, int uid, String packageName) { @@ -224,64 +222,13 @@ public class AppOpsControllerImpl implements AppOpsController, createdNew = true; } } + // We should keep this so we make sure it cannot time out. + mBGHandler.removeCallbacksAndMessages(item); mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS); return createdNew; } /** - * Does the app-op code refer to a user sensitive permission for the specified user id - * and package. Only user sensitive permission should be shown to the user by default. - * - * @param appOpCode The code of the app-op. - * @param uid The uid of the user. - * @param packageName The name of the package. - * - * @return {@code true} iff the app-op item is user sensitive - */ - private boolean isUserSensitive(int appOpCode, int uid, String packageName) { - String permission = AppOpsManager.opToPermission(appOpCode); - if (permission == null) { - return false; - } - int permFlags = mFlagsCache.getPermissionFlags(permission, - packageName, UserHandle.getUserHandleForUid(uid)); - return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0; - } - - /** - * Does the app-op item refer to an operation that should be shown to the user. - * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive - * permission should be shown to the user by default. - * - * @param item The item - * - * @return {@code true} iff the app-op item should be shown to the user - */ - private boolean isUserVisible(AppOpItem item) { - return isUserVisible(item.getCode(), item.getUid(), item.getPackageName()); - } - - - /** - * Does the app-op, uid and package name, refer to an operation that should be shown to the - * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or - * ops that refer to user sensitive permission should be shown to the user by default. - * - * @param item The item - * - * @return {@code true} iff the app-op for should be shown to the user - */ - private boolean isUserVisible(int appOpCode, int uid, String packageName) { - // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission - // which may be user senstive, so for now always show it to the user. - if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { - return true; - } - - return isUserSensitive(appOpCode, uid, packageName); - } - - /** * Returns a copy of the list containing all the active AppOps that the controller tracks. * * @return List of active AppOps information @@ -304,8 +251,8 @@ public class AppOpsControllerImpl implements AppOpsController, final int numActiveItems = mActiveItems.size(); for (int i = 0; i < numActiveItems; i++) { AppOpItem item = mActiveItems.get(i); - if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item)) { + if ((userId == UserHandle.USER_ALL + || UserHandle.getUserId(item.getUid()) == userId)) { list.add(item); } } @@ -314,8 +261,8 @@ public class AppOpsControllerImpl implements AppOpsController, final int numNotedItems = mNotedItems.size(); for (int i = 0; i < numNotedItems; i++) { AppOpItem item = mNotedItems.get(i); - if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item)) { + if ((userId == UserHandle.USER_ALL + || UserHandle.getUserId(item.getUid()) == userId)) { list.add(item); } } @@ -325,7 +272,21 @@ public class AppOpsControllerImpl implements AppOpsController, @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { - if (updateActives(code, uid, packageName, active)) { + if (DEBUG) { + Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s", code, uid, packageName, + Boolean.toString(active))); + } + boolean activeChanged = updateActives(code, uid, packageName, active); + if (!activeChanged) return; // early return + // Check if the item is also noted, in that case, there's no update. + boolean alsoNoted; + synchronized (mNotedItems) { + alsoNoted = getAppOpItemLocked(mNotedItems, code, uid, packageName) != null; + } + // If active is true, we only send the update if the op is not actively noted (already true) + // If active is false, we only send the update if the op is not actively noted (prevent + // early removal) + if (!alsoNoted) { mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active)); } } @@ -333,17 +294,23 @@ public class AppOpsControllerImpl implements AppOpsController, @Override public void onOpNoted(int code, int uid, String packageName, int result) { if (DEBUG) { - Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]); + Log.w(TAG, "Noted op: " + code + " with result " + + AppOpsManager.MODE_NAMES[result] + " for package " + packageName); } if (result != AppOpsManager.MODE_ALLOWED) return; - if (addNoted(code, uid, packageName)) { + boolean notedAdded = addNoted(code, uid, packageName); + if (!notedAdded) return; // early return + boolean alsoActive; + synchronized (mActiveItems) { + alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; + } + if (!alsoActive) { mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true)); } } private void notifySuscribers(int code, int uid, String packageName, boolean active) { - if (mCallbacksByCode.containsKey(code) - && isUserVisible(code, uid, packageName)) { + if (mCallbacksByCode.containsKey(code)) { if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { cb.onActiveStateChanged(code, uid, packageName, active); @@ -368,7 +335,7 @@ public class AppOpsControllerImpl implements AppOpsController, } - protected final class H extends Handler { + protected class H extends Handler { H(Looper looper) { super(looper); } diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt deleted file mode 100644 index f02c7afdb9ca..000000000000 --- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.appops - -import android.content.Context -import android.content.pm.PackageManager -import android.os.UserHandle -import android.util.ArrayMap -import com.android.internal.annotations.VisibleForTesting - -private data class PermissionFlag(val flag: Int, val timestamp: Long) - -private data class PermissionFlagKey( - val permission: String, - val packageName: String, - val user: UserHandle -) - -internal const val CACHE_EXPIRATION = 10000L - -/** - * Cache for PackageManager's PermissionFlags. - * - * Flags older than [CACHE_EXPIRATION] will be retrieved again. - */ -internal open class PermissionFlagsCache(context: Context) { - private val packageManager = context.packageManager - private val permissionFlagsCache = ArrayMap<PermissionFlagKey, PermissionFlag>() - - /** - * Retrieve permission flags from cache or PackageManager. There parameters will be passed - * directly to [PackageManager]. - * - * Calls to this method should be done from a background thread. - */ - fun getPermissionFlags(permission: String, packageName: String, user: UserHandle): Int { - val key = PermissionFlagKey(permission, packageName, user) - val now = getCurrentTime() - val value = permissionFlagsCache.getOrPut(key) { - PermissionFlag(getFlags(key), now) - } - if (now - value.timestamp > CACHE_EXPIRATION) { - val newValue = PermissionFlag(getFlags(key), now) - permissionFlagsCache.put(key, newValue) - return newValue.flag - } else { - return value.flag - } - } - - private fun getFlags(key: PermissionFlagKey) = - packageManager.getPermissionFlags(key.permission, key.packageName, key.user) - - @VisibleForTesting - protected open fun getCurrentTime() = System.currentTimeMillis() -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index fc19fa0c55fc..91bb80c60e3f 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1166,13 +1166,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, TextView messageView = (TextView) v.findViewById(R.id.message); messageView.setSelected(true); // necessary for marquee to work - TextView statusView = (TextView) v.findViewById(R.id.status); - final String status = getStatus(); - if (!TextUtils.isEmpty(status)) { - statusView.setText(status); - } else { - statusView.setVisibility(View.GONE); - } if (mIcon != null) { icon.setImageDrawable(mIcon); icon.setScaleType(ScaleType.CENTER_CROP); @@ -1257,32 +1250,26 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, LayoutInflater inflater) { willCreate(); - View v = inflater.inflate(R - .layout.global_actions_item, parent, false); + View v = inflater.inflate(com.android.systemui.R + .layout.global_actions_grid_item, parent, false); ImageView icon = (ImageView) v.findViewById(R.id.icon); TextView messageView = (TextView) v.findViewById(R.id.message); - TextView statusView = (TextView) v.findViewById(R.id.status); final boolean enabled = isEnabled(); + boolean on = ((mState == State.On) || (mState == State.TurningOn)); if (messageView != null) { - messageView.setText(mMessageResId); + messageView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); messageView.setEnabled(enabled); messageView.setSelected(true); // necessary for marquee to work } - boolean on = ((mState == State.On) || (mState == State.TurningOn)); if (icon != null) { icon.setImageDrawable(context.getDrawable( (on ? mEnabledIconResId : mDisabledIconResid))); icon.setEnabled(enabled); } - if (statusView != null) { - statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); - statusView.setVisibility(View.VISIBLE); - statusView.setEnabled(enabled); - } v.setEnabled(enabled); return v; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 02c4beb7ac1b..9f2bbc680897 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -20,7 +20,7 @@ import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED; import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT; import android.animation.Animator; @@ -246,20 +246,13 @@ public class GlobalScreenshot { mSaveInBgTask.cancel(false); } - if (!DeviceConfig.getBoolean( - NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false)) { - mNotificationsController.reset(); - mNotificationsController.setImage(mScreenBitmap); - mNotificationsController.showSavingScreenshotNotification(); - } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute(); } /** * Takes a screenshot of the current display and shows an animation. */ - private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, - boolean navBarVisible, Rect crop) { + private void takeScreenshot(Consumer<Uri> finisher, Rect crop) { int rot = mDisplay.getRotation(); int width = crop.width(); int height = crop.height(); @@ -278,21 +271,20 @@ public class GlobalScreenshot { mScreenBitmap.prepareToDraw(); // Start the post-screenshot animation - startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, - statusBarVisible, navBarVisible); + startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels); } - void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) { + void takeScreenshot(Consumer<Uri> finisher) { mDisplay.getRealMetrics(mDisplayMetrics); - takeScreenshot(finisher, statusBarVisible, navBarVisible, + takeScreenshot( + finisher, new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); } /** * Displays a screenshot selector */ - void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible, - final boolean navBarVisible) { + void takeScreenshotPartial(final Consumer<Uri> finisher) { mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -312,8 +304,7 @@ public class GlobalScreenshot { if (rect != null) { if (rect.width() != 0 && rect.height() != 0) { // Need mScreenshotLayout to handle it after the view disappears - mScreenshotLayout.post(() -> takeScreenshot( - finisher, statusBarVisible, navBarVisible, rect)); + mScreenshotLayout.post(() -> takeScreenshot(finisher, rect)); } } @@ -364,8 +355,7 @@ public class GlobalScreenshot { /** * Starts the animation after taking the screenshot */ - private void startAnimation(final Consumer<Uri> finisher, int w, int h, - boolean statusBarVisible, boolean navBarVisible) { + private void startAnimation(final Consumer<Uri> finisher, int w, int h) { // If power save is on, show a toast so there is some visual indication that a screenshot // has been taken. PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -385,50 +375,30 @@ public class GlobalScreenshot { mScreenshotAnimation.removeAllListeners(); } - boolean useCornerFlow = - DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false); mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); - ValueAnimator screenshotFadeOutAnim = useCornerFlow - ? createScreenshotToCornerAnimation(w, h) - : createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible); + ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h); mScreenshotAnimation = new AnimatorSet(); mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim); mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // Save the screenshot once we have a bit of time now - if (!useCornerFlow) { - saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { - @Override - void onActionsReady(Uri uri, List<Notification.Action> actions) { - if (uri == null) { - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_capture_text); - } else { - mNotificationsController - .showScreenshotActionsNotification(uri, actions); - } - } - }); - clearScreenshot(); - } else { - saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { - @Override - void onActionsReady(Uri uri, List<Notification.Action> actions) { - if (uri == null) { - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_capture_text); - } else { - mScreenshotHandler.post(() -> - createScreenshotActionsShadeAnimation(actions).start()); - } + saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { + @Override + void onActionsReady(Uri uri, List<Notification.Action> actions) { + if (uri == null) { + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_capture_text); + } else { + mScreenshotHandler.post(() -> + createScreenshotActionsShadeAnimation(actions).start()); } - }); - mScreenshotHandler.sendMessageDelayed( - mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT), - SCREENSHOT_CORNER_TIMEOUT_MILLIS); - } + } + }); + mScreenshotHandler.sendMessageDelayed( + mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT), + SCREENSHOT_CORNER_TIMEOUT_MILLIS); } }); mScreenshotHandler.post(() -> { @@ -492,7 +462,7 @@ public class GlobalScreenshot { } @Override - public void onAnimationEnd(android.animation.Animator animation) { + public void onAnimationEnd(Animator animation) { mScreenshotFlash.setVisibility(View.GONE); } }); @@ -513,81 +483,6 @@ public class GlobalScreenshot { return anim; } - private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible, - boolean navBarVisible) { - ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); - anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mBackgroundView.setVisibility(View.GONE); - mScreenshotView.setVisibility(View.GONE); - mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); - } - }); - - if (!statusBarVisible || !navBarVisible) { - // There is no status bar/nav bar, so just fade the screenshot away in place - anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION); - anim.addUpdateListener(new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = (Float) animation.getAnimatedValue(); - float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) - - t * (SCREENSHOT_DROP_IN_MIN_SCALE - - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE); - mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); - mScreenshotView.setAlpha(1f - t); - mScreenshotView.setScaleX(scaleT); - mScreenshotView.setScaleY(scaleT); - } - }); - } else { - // In the case where there is a status bar, animate to the origin of the bar (top-left) - final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION - / SCREENSHOT_DROP_OUT_DURATION; - final Interpolator scaleInterpolator = new Interpolator() { - @Override - public float getInterpolation(float x) { - if (x < scaleDurationPct) { - // Decelerate, and scale the input accordingly - return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f)); - } - return 1f; - } - }; - - // Determine the bounds of how to scale - float halfScreenWidth = (w - 2f * mBgPadding) / 2f; - float halfScreenHeight = (h - 2f * mBgPadding) / 2f; - final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET; - final PointF finalPos = new PointF( - -halfScreenWidth - + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth, - -halfScreenHeight - + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight); - - // Animate the screenshot to the status bar - anim.setDuration(SCREENSHOT_DROP_OUT_DURATION); - anim.addUpdateListener(new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = (Float) animation.getAnimatedValue(); - float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) - - scaleInterpolator.getInterpolation(t) - * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE); - mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); - mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t)); - mScreenshotView.setScaleX(scaleT); - mScreenshotView.setScaleY(scaleT); - mScreenshotView.setTranslationX(t * finalPos.x); - mScreenshotView.setTranslationY(t * finalPos.y); - } - }); - } - return anim; - } - private ValueAnimator createScreenshotToCornerAnimation(int w, int h) { ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY); @@ -648,13 +543,16 @@ public class GlobalScreenshot { }); mActionsView.addView(actionChip); } - TextView scrollChip = (TextView) inflater.inflate( - R.layout.global_screenshot_action_chip, mActionsView, false); - Toast scrollNotImplemented = Toast.makeText( - mContext, "Not implemented", Toast.LENGTH_SHORT); - scrollChip.setText("Scroll"); // TODO (mkephart): add resource and translate - scrollChip.setOnClickListener(v -> scrollNotImplemented.show()); - mActionsView.addView(scrollChip); + + if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) { + TextView scrollChip = (TextView) inflater.inflate( + R.layout.global_screenshot_action_chip, mActionsView, false); + Toast scrollNotImplemented = Toast.makeText( + mContext, "Not implemented", Toast.LENGTH_SHORT); + scrollChip.setText("Scroll"); // TODO (mkephart): add resource and translate + scrollChip.setOnClickListener(v -> scrollNotImplemented.show()); + mActionsView.addView(scrollChip); + } ValueAnimator animator = ValueAnimator.ofFloat(0, 1); mActionsView.setY(mDisplayMetrics.heightPixels); @@ -776,8 +674,7 @@ public class GlobalScreenshot { String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE); Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent); ActivityOptions opts = ActivityOptions.makeBasic(); - context.startActivityAsUser(actionIntent, opts.toBundle(), - UserHandle.CURRENT); + context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT); ScreenshotSmartActions.notifyScreenshotAction( context, intent.getStringExtra(EXTRA_ID), actionType, true); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java new file mode 100644 index 000000000000..11aa80bbea35 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.screenshot; + +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.Nullable; +import android.app.Notification; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.PointF; +import android.graphics.Rect; +import android.media.MediaActionSound; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.PowerManager; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.Interpolator; +import android.widget.ImageView; +import android.widget.Toast; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.List; +import java.util.function.Consumer; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Class for handling device screen shots + * + * @deprecated will be removed when corner flow is complete and tested + */ +@Singleton +@Deprecated +public class GlobalScreenshotLegacy { + + // These strings are used for communicating the action invoked to + // ScreenshotNotificationSmartActionsProvider. + static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; + static final String EXTRA_ID = "android:screenshot_id"; + static final String ACTION_TYPE_DELETE = "Delete"; + static final String ACTION_TYPE_SHARE = "Share"; + static final String ACTION_TYPE_EDIT = "Edit"; + static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled"; + static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent"; + + static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; + static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification"; + static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip"; + + private static final String TAG = "GlobalScreenshot"; + + private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130; + private static final int SCREENSHOT_DROP_IN_DURATION = 430; + private static final int SCREENSHOT_DROP_OUT_DELAY = 500; + private static final int SCREENSHOT_DROP_OUT_DURATION = 430; + private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370; + private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320; + private static final float BACKGROUND_ALPHA = 0.5f; + private static final float SCREENSHOT_SCALE = 1f; + private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f; + private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f; + private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f; + private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f; + + private final ScreenshotNotificationsController mNotificationsController; + + private Context mContext; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mWindowLayoutParams; + private Display mDisplay; + private DisplayMetrics mDisplayMetrics; + + private Bitmap mScreenBitmap; + private View mScreenshotLayout; + private ScreenshotSelectorView mScreenshotSelectorView; + private ImageView mBackgroundView; + private ImageView mScreenshotView; + private ImageView mScreenshotFlash; + + private AnimatorSet mScreenshotAnimation; + + private float mBgPadding; + private float mBgPaddingScale; + + private AsyncTask<Void, Void, Void> mSaveInBgTask; + + private MediaActionSound mCameraSound; + + /** + * @param context everything needs a context :( + */ + @Inject + public GlobalScreenshotLegacy( + Context context, @Main Resources resources, LayoutInflater layoutInflater, + ScreenshotNotificationsController screenshotNotificationsController) { + mContext = context; + mNotificationsController = screenshotNotificationsController; + + // Inflate the screenshot layout + mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot_legacy, null); + mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_background); + mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy); + + mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_flash); + mScreenshotSelectorView = mScreenshotLayout.findViewById( + R.id.global_screenshot_legacy_selector); + mScreenshotLayout.setFocusable(true); + mScreenshotSelectorView.setFocusable(true); + mScreenshotSelectorView.setFocusableInTouchMode(true); + mScreenshotLayout.setOnTouchListener((v, event) -> { + // Intercept and ignore all touch events + return true; + }); + + // Setup the window that we are going to use + mWindowLayoutParams = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, + WindowManager.LayoutParams.TYPE_SCREENSHOT, + WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, + PixelFormat.TRANSLUCENT); + mWindowLayoutParams.setTitle("ScreenshotAnimation"); + mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */); + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mDisplay = mWindowManager.getDefaultDisplay(); + mDisplayMetrics = new DisplayMetrics(); + mDisplay.getRealMetrics(mDisplayMetrics); + + // Scale has to account for both sides of the bg + mBgPadding = (float) resources.getDimensionPixelSize( + R.dimen.global_screenshot_legacy_bg_padding); + mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels; + + + // Setup the Camera shutter sound + mCameraSound = new MediaActionSound(); + mCameraSound.load(MediaActionSound.SHUTTER_CLICK); + } + + /** + * Creates a new worker thread and saves the screenshot to the media store. + */ + private void saveScreenshotInWorkerThread( + Consumer<Uri> finisher, + @Nullable GlobalScreenshot.ActionsReadyListener actionsReadyListener) { + GlobalScreenshot.SaveImageInBackgroundData data = + new GlobalScreenshot.SaveImageInBackgroundData(); + data.image = mScreenBitmap; + data.finisher = finisher; + data.mActionsReadyListener = actionsReadyListener; + if (mSaveInBgTask != null) { + mSaveInBgTask.cancel(false); + } + + mNotificationsController.reset(); + mNotificationsController.setImage(mScreenBitmap); + mNotificationsController.showSavingScreenshotNotification(); + + mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute(); + } + + /** + * Takes a screenshot of the current display and shows an animation. + */ + private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, + boolean navBarVisible, Rect crop) { + int rot = mDisplay.getRotation(); + int width = crop.width(); + int height = crop.height(); + + // Take the screenshot + mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot); + if (mScreenBitmap == null) { + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_capture_text); + finisher.accept(null); + return; + } + + // Optimizations + mScreenBitmap.setHasAlpha(false); + mScreenBitmap.prepareToDraw(); + + // Start the post-screenshot animation + startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, + statusBarVisible, navBarVisible); + } + + void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) { + mDisplay.getRealMetrics(mDisplayMetrics); + takeScreenshot(finisher, statusBarVisible, navBarVisible, + new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); + } + + /** + * Displays a screenshot selector + */ + void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible, + final boolean navBarVisible) { + mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); + mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + ScreenshotSelectorView view = (ScreenshotSelectorView) v; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + view.startSelection((int) event.getX(), (int) event.getY()); + return true; + case MotionEvent.ACTION_MOVE: + view.updateSelection((int) event.getX(), (int) event.getY()); + return true; + case MotionEvent.ACTION_UP: + view.setVisibility(View.GONE); + mWindowManager.removeView(mScreenshotLayout); + final Rect rect = view.getSelectionRect(); + if (rect != null) { + if (rect.width() != 0 && rect.height() != 0) { + // Need mScreenshotLayout to handle it after the view disappears + mScreenshotLayout.post(() -> takeScreenshot( + finisher, statusBarVisible, navBarVisible, rect)); + } + } + + view.stopSelection(); + return true; + } + + return false; + } + }); + mScreenshotLayout.post(new Runnable() { + @Override + public void run() { + mScreenshotSelectorView.setVisibility(View.VISIBLE); + mScreenshotSelectorView.requestFocus(); + } + }); + } + + /** + * Cancels screenshot request + */ + void stopScreenshot() { + // If the selector layer still presents on screen, we remove it and resets its state. + if (mScreenshotSelectorView.getSelectionRect() != null) { + mWindowManager.removeView(mScreenshotLayout); + mScreenshotSelectorView.stopSelection(); + } + } + + /** + * Clears current screenshot + */ + private void clearScreenshot() { + if (mScreenshotLayout.isAttachedToWindow()) { + mWindowManager.removeView(mScreenshotLayout); + } + + // Clear any references to the bitmap + mScreenBitmap = null; + mScreenshotView.setImageBitmap(null); + mBackgroundView.setVisibility(View.GONE); + mScreenshotView.setVisibility(View.GONE); + mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); + } + + /** + * Starts the animation after taking the screenshot + */ + private void startAnimation(final Consumer<Uri> finisher, int w, int h, + boolean statusBarVisible, boolean navBarVisible) { + // If power save is on, show a toast so there is some visual indication that a screenshot + // has been taken. + PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + if (powerManager.isPowerSaveMode()) { + Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show(); + } + + // Add the view for the animation + mScreenshotView.setImageBitmap(mScreenBitmap); + mScreenshotLayout.requestFocus(); + + // Setup the animation with the screenshot just taken + if (mScreenshotAnimation != null) { + if (mScreenshotAnimation.isStarted()) { + mScreenshotAnimation.end(); + } + mScreenshotAnimation.removeAllListeners(); + } + + mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); + ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); + ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, + statusBarVisible, navBarVisible); + mScreenshotAnimation = new AnimatorSet(); + mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim); + mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Save the screenshot once we have a bit of time now + saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() { + @Override + void onActionsReady(Uri uri, List<Notification.Action> actions) { + if (uri == null) { + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_capture_text); + } else { + mNotificationsController.showScreenshotActionsNotification( + uri, actions); + } + } + }); + clearScreenshot(); + } + }); + mScreenshotLayout.post(() -> { + // Play the shutter sound to notify that we've taken a screenshot + mCameraSound.play(MediaActionSound.SHUTTER_CLICK); + + mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mScreenshotView.buildLayer(); + mScreenshotAnimation.start(); + }); + } + + private ValueAnimator createScreenshotDropInAnimation() { + final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION) + / SCREENSHOT_DROP_IN_DURATION); + final float flashDurationPct = 2f * flashPeakDurationPct; + final Interpolator flashAlphaInterpolator = new Interpolator() { + @Override + public float getInterpolation(float x) { + // Flash the flash view in and out quickly + if (x <= flashDurationPct) { + return (float) Math.sin(Math.PI * (x / flashDurationPct)); + } + return 0; + } + }; + final Interpolator scaleInterpolator = new Interpolator() { + @Override + public float getInterpolation(float x) { + // We start scaling when the flash is at it's peak + if (x < flashPeakDurationPct) { + return 0; + } + return (x - flashDurationPct) / (1f - flashDurationPct); + } + }; + + Resources r = mContext.getResources(); + if ((r.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES) { + mScreenshotView.getBackground().setTint(Color.BLACK); + } else { + mScreenshotView.getBackground().setTintList(null); + } + + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setDuration(SCREENSHOT_DROP_IN_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBackgroundView.setAlpha(0f); + mBackgroundView.setVisibility(View.VISIBLE); + mScreenshotView.setAlpha(0f); + mScreenshotView.setTranslationX(0f); + mScreenshotView.setTranslationY(0f); + mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale); + mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale); + mScreenshotView.setVisibility(View.VISIBLE); + mScreenshotFlash.setAlpha(0f); + mScreenshotFlash.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationEnd(android.animation.Animator animation) { + mScreenshotFlash.setVisibility(View.GONE); + } + }); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (Float) animation.getAnimatedValue(); + float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale) + - scaleInterpolator.getInterpolation(t) + * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE); + mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(t); + mScreenshotView.setScaleX(scaleT); + mScreenshotView.setScaleY(scaleT); + mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t)); + } + }); + return anim; + } + + private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible, + boolean navBarVisible) { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBackgroundView.setVisibility(View.GONE); + mScreenshotView.setVisibility(View.GONE); + mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + + if (!statusBarVisible || !navBarVisible) { + // There is no status bar/nav bar, so just fade the screenshot away in place + anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (Float) animation.getAnimatedValue(); + float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) + - t * (SCREENSHOT_DROP_IN_MIN_SCALE + - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE); + mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(1f - t); + mScreenshotView.setScaleX(scaleT); + mScreenshotView.setScaleY(scaleT); + } + }); + } else { + // In the case where there is a status bar, animate to the origin of the bar (top-left) + final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION + / SCREENSHOT_DROP_OUT_DURATION; + final Interpolator scaleInterpolator = new Interpolator() { + @Override + public float getInterpolation(float x) { + if (x < scaleDurationPct) { + // Decelerate, and scale the input accordingly + return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f)); + } + return 1f; + } + }; + + // Determine the bounds of how to scale + float halfScreenWidth = (w - 2f * mBgPadding) / 2f; + float halfScreenHeight = (h - 2f * mBgPadding) / 2f; + final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET; + final PointF finalPos = new PointF( + -halfScreenWidth + + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth, + -halfScreenHeight + + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight); + + // Animate the screenshot to the status bar + anim.setDuration(SCREENSHOT_DROP_OUT_DURATION); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (Float) animation.getAnimatedValue(); + float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) + - scaleInterpolator.getInterpolation(t) + * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE); + mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t)); + mScreenshotView.setScaleX(scaleT); + mScreenshotView.setScaleY(scaleT); + mScreenshotView.setTranslationX(t * finalPos.x); + mScreenshotView.setTranslationY(t * finalPos.y); + } + }); + } + return anim; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 29b96a90a734..4f045d5c1b71 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -16,15 +16,21 @@ package com.android.systemui.screenshot; +import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; + +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW; + import android.app.Service; import android.content.Intent; import android.net.Uri; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.os.UserManager; +import android.provider.DeviceConfig; import android.util.Log; import android.view.WindowManager; @@ -36,9 +42,10 @@ public class TakeScreenshotService extends Service { private static final String TAG = "TakeScreenshotService"; private final GlobalScreenshot mScreenshot; + private final GlobalScreenshotLegacy mScreenshotLegacy; private final UserManager mUserManager; - private Handler mHandler = new Handler() { + private Handler mHandler = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { final Messenger callback = msg.replyTo; @@ -59,12 +66,24 @@ public class TakeScreenshotService extends Service { return; } + // TODO (mkephart): clean up once notifications flow is fully deprecated + boolean useCornerFlow = DeviceConfig.getBoolean( + NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false); switch (msg.what) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: - mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0); + if (useCornerFlow) { + mScreenshot.takeScreenshot(finisher); + } else { + mScreenshotLegacy.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0); + } break; case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: - mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0); + if (useCornerFlow) { + mScreenshot.takeScreenshotPartial(finisher); + } else { + mScreenshotLegacy.takeScreenshotPartial( + finisher, msg.arg1 > 0, msg.arg2 > 0); + } break; default: Log.d(TAG, "Invalid screenshot option: " + msg.what); @@ -73,8 +92,10 @@ public class TakeScreenshotService extends Service { }; @Inject - public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager) { + public TakeScreenshotService(GlobalScreenshot globalScreenshot, + GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager) { mScreenshot = globalScreenshot; + mScreenshotLegacy = globalScreenshotLegacy; mUserManager = userManager; } @@ -86,6 +107,7 @@ public class TakeScreenshotService extends Service { @Override public boolean onUnbind(Intent intent) { if (mScreenshot != null) mScreenshot.stopScreenshot(); + if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot(); return true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index af0ef8298a3f..e5ae1aacc771 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -25,11 +25,11 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; + +import static java.lang.Thread.sleep; import android.app.AppOpsManager; import android.content.pm.PackageManager; @@ -57,7 +57,6 @@ public class AppOpsControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = UserHandle.getUid(0, 0); private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0); - private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0); @Mock private AppOpsManager mAppOpsManager; @@ -68,8 +67,6 @@ public class AppOpsControllerTest extends SysuiTestCase { @Mock private AppOpsControllerImpl.H mMockHandler; @Mock - private PermissionFlagsCache mFlagsCache; - @Mock private DumpController mDumpController; private AppOpsControllerImpl mController; @@ -85,17 +82,9 @@ public class AppOpsControllerTest extends SysuiTestCase { // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of // TEST_UID_NON_USER_SENSITIVE are user sensitive. getContext().setMockPackageManager(mPackageManager); - when(mFlagsCache.getPermissionFlags(anyString(), anyString(), - eq(UserHandle.getUserHandleForUid(TEST_UID)))).thenReturn( - PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED); - when(mFlagsCache.getPermissionFlags(anyString(), anyString(), - eq(UserHandle.getUserHandleForUid(TEST_UID_OTHER)))).thenReturn( - PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED); - when(mFlagsCache.getPermissionFlags(anyString(), anyString(), - eq(UserHandle.getUserHandleForUid(TEST_UID_NON_USER_SENSITIVE)))).thenReturn(0); - mController = new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mFlagsCache, - mDumpController); + mController = + new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpController); } @Test @@ -191,14 +180,6 @@ public class AppOpsControllerTest extends SysuiTestCase { } @Test - public void nonUserSensitiveOpsAreIgnored() { - mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, - TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true); - assertEquals(0, mController.getActiveAppOpsForUser( - UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size()); - } - - @Test public void opNotedScheduledForRemoval() { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, @@ -251,4 +232,100 @@ public class AppOpsControllerTest extends SysuiTestCase { // Only one post to notify subscribers verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong()); } + + @Test + public void testActiveOpNotRemovedAfterNoted() throws InterruptedException { + // Replaces the timeout delay with 5 ms + AppOpsControllerImpl.H testHandler = mController.new H(mTestableLooper.getLooper()) { + @Override + public void scheduleRemoval(AppOpItem item, long timeToRemoval) { + super.scheduleRemoval(item, 5L); + } + }; + + mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + mController.setBGHandler(testHandler); + + mController.onOpActiveChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + AppOpsManager.MODE_ALLOWED); + + mTestableLooper.processAllMessages(); + List<AppOpItem> list = mController.getActiveAppOps(); + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + + // Duplicates are not removed between active and noted + assertEquals(2, list.size()); + + sleep(10L); + + mTestableLooper.processAllMessages(); + + verify(mCallback, never()).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false); + list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + } + + @Test + public void testNotedNotRemovedAfterActive() { + mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + AppOpsManager.MODE_ALLOWED); + + mController.onOpActiveChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + + mTestableLooper.processAllMessages(); + List<AppOpItem> list = mController.getActiveAppOps(); + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + + // Duplicates are not removed between active and noted + assertEquals(2, list.size()); + + mController.onOpActiveChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false); + + mTestableLooper.processAllMessages(); + + verify(mCallback, never()).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false); + list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + } + + @Test + public void testNotedAndActiveOnlyOneCall() { + mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + AppOpsManager.MODE_ALLOWED); + + mController.onOpActiveChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + + mTestableLooper.processAllMessages(); + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + } + + @Test + public void testActiveAndNotedOnlyOneCall() { + mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + + mController.onOpActiveChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + AppOpsManager.MODE_ALLOWED); + + mTestableLooper.processAllMessages(); + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt deleted file mode 100644 index dc070dea6d89..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.appops - -import android.content.Context -import android.content.pm.PackageManager -import android.os.UserHandle -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -class PermissionFlagsCacheTest : SysuiTestCase() { - - companion object { - const val TEST_PERMISSION = "test_permission" - const val TEST_PACKAGE = "test_package" - } - - @Mock - private lateinit var mPackageManager: PackageManager - @Mock - private lateinit var mUserHandle: UserHandle - private lateinit var flagsCache: TestPermissionFlagsCache - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - mContext.setMockPackageManager(mPackageManager) - flagsCache = TestPermissionFlagsCache(mContext) - } - - @Test - fun testCallsPackageManager_exactlyOnce() { - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - flagsCache.time = CACHE_EXPIRATION - 1 - verify(mPackageManager).getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - } - - @Test - fun testCallsPackageManager_cacheExpired() { - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - flagsCache.time = CACHE_EXPIRATION + 1 - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - verify(mPackageManager, times(2)) - .getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - } - - @Test - fun testCallsPackageMaanger_multipleKeys() { - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - flagsCache.getPermissionFlags(TEST_PERMISSION, "", mUserHandle) - verify(mPackageManager, times(2)) - .getPermissionFlags(anyString(), anyString(), any()) - } - - private class TestPermissionFlagsCache(context: Context) : PermissionFlagsCache(context) { - var time = 0L - - override fun getCurrentTime(): Long { - return time - } - } -}
\ No newline at end of file diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml index e99c2c529bd2..5a71eb23abad 100644 --- a/packages/Tethering/AndroidManifest.xml +++ b/packages/Tethering/AndroidManifest.xml @@ -32,6 +32,7 @@ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.MANAGE_USB" /> <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 37e679dbeb63..ca290c637773 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -1,7 +1,152 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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> <!-- OEMs that wish to change the below settings must do so via a runtime resource overlay package and *NOT* by changing this file. This file is part of the tethering mainline module. + TODO: define two resources for each config item: a default_* resource and a config_* resource, + config_* is empty by default but may be overridden by RROs. --> + <!-- List of regexpressions describing the interface (if any) that represent tetherable + USB interfaces. If the device doesn't want to support tethering over USB this should + be empty. An example would be "usb.*" --> + <string-array translatable="false" name="config_tether_usb_regexs"> + <item>"usb\\d"</item> + <item>"rndis\\d"</item> + </string-array> + + <!-- List of regexpressions describing the interface (if any) that represent tetherable + Wifi interfaces. If the device doesn't want to support tethering over Wifi this + should be empty. An example would be "softap.*" --> + <string-array translatable="false" name="config_tether_wifi_regexs"> + <item>"wlan\\d"</item> + <item>"softap\\d"</item> + </string-array> + + <!-- List of regexpressions describing the interface (if any) that represent tetherable + Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this + should be empty. An example would be "p2p-p2p.*" --> + <string-array translatable="false" name="config_tether_wifi_p2p_regexs"> + </string-array> + + <!-- List of regexpressions describing the interface (if any) that represent tetherable + bluetooth interfaces. If the device doesn't want to support tethering over bluetooth this + should be empty. --> + <string-array translatable="false" name="config_tether_bluetooth_regexs"> + <item>"bt-pan"</item> + </string-array> + + <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. --> + <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool> + + <!-- Dhcp range (min, max) to use for tethering purposes --> + <string-array translatable="false" name="config_tether_dhcp_range"> + </string-array> + + <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI, + WIFI} values allowable for tethering. + + Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or + [1,7,0] for TYPE_WIFI, TYPE_BLUETOOTH, and TYPE_MOBILE. + + This list is also modified by code within the framework, including: + + - TYPE_ETHERNET (9) is prepended to this list, and + + - the return value of TelephonyManager.isTetheringApnRequired() + determines how the array is further modified: + + * TRUE (DUN REQUIRED). + TYPE_MOBILE is removed (if present). + TYPE_MOBILE_HIPRI is removed (if present). + TYPE_MOBILE_DUN is appended (if not already present). + + * FALSE (DUN NOT REQUIRED). + TYPE_MOBILE_DUN is removed (if present). + If both of TYPE_MOBILE{,_HIPRI} are not present: + TYPE_MOBILE is appended. + TYPE_MOBILE_HIPRI is appended. + + For other changes applied to this list, now and in the future, see + com.android.server.connectivity.tethering.TetheringConfiguration. + + Note also: the order of this is important. The first upstream type + for which a satisfying network exists is used. + --> + <integer-array translatable="false" name="config_tether_upstream_types"> + </integer-array> + + <!-- When true, the tethering upstream network follows the current default + Internet network (except when the current default network is mobile, + in which case a DUN network will be used if required). + + When true, overrides the config_tether_upstream_types setting above. + --> + <bool translatable="false" name="config_tether_upstream_automatic">true</bool> + + + <!-- If the mobile hotspot feature requires provisioning, a package name and class name + can be provided to launch a supported application that provisions the devices. + EntitlementManager will send an inent to Settings with the specified package name and + class name in extras to launch provision app. + TODO: note what extras here. + + See EntitlementManager#runUiTetherProvisioning and + packages/apps/Settings/src/com/android/settings/network/TetherProvisioningActivity.java + for more details. + + For ui-less/periodic recheck support see config_mobile_hotspot_provision_app_no_ui + --> + <!-- The first element is the package name and the second element is the class name + of the provisioning app --> + <string-array translatable="false" name="config_mobile_hotspot_provision_app"> + <!-- + <item>com.example.provisioning</item> + <item>com.example.provisioning.Activity</item> + --> + </string-array> + + <!-- If the mobile hotspot feature requires provisioning, an action can be provided + that will be broadcast in non-ui cases for checking the provisioning status. + EntitlementManager will pass the specified name to Settings and Settings would + launch provisioning app by sending an intent with the package name. + + A second broadcast, action defined by config_mobile_hotspot_provision_response, + will be sent back to notify if provisioning succeeded or not. The response will + match that of the activity in config_mobile_hotspot_provision_app, but instead + contained within the int extra "EntitlementResult". + TODO: provide the system api for "EntitlementResult" extra and note it here. + + See EntitlementManager#runSilentTetherProvisioning and + packages/apps/Settings/src/com/android/settings/wifi/tether/TetherService.java for more + details. + --> + <string translatable="false" name="config_mobile_hotspot_provision_app_no_ui"></string> + + <!-- Sent in response to a provisioning check. The caller must hold the + permission android.permission.TETHER_PRIVILEGED for Settings to + receive this response. + + See config_mobile_hotspot_provision_response + --> + <string translatable="false" name="config_mobile_hotspot_provision_response"></string> + + <!-- Number of hours between each background provisioning call --> + <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer> + + <!-- ComponentName of the service used to run no ui tether provisioning. --> + <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string> </resources> diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml new file mode 100644 index 000000000000..e089d9d19950 --- /dev/null +++ b/packages/Tethering/res/values/overlayable.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"> + <overlayable name="TetheringConfig"> + <policy type="product|system|vendor"> + <item type="array" name="config_tether_usb_regexs"/> + <item type="array" name="config_tether_wifi_regexs"/> + <item type="array" name="config_tether_wifi_p2p_regexs"/> + <item type="array" name="config_tether_bluetooth_regexs"/> + <item type="array" name="config_tether_dhcp_range"/> + <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> + <item type="array" name="config_tether_upstream_types"/> + <item type="bool" name="config_tether_upstream_automatic"/> + <!-- Configuration values for tethering entitlement check --> + <item type="array" name="config_mobile_hotspot_provision_app"/> + <item type="string" name="config_mobile_hotspot_provision_app_no_ui"/> + <item type="string" name="config_mobile_hotspot_provision_response"/> + <item type="integer" name="config_mobile_hotspot_provision_check_period"/> + <item type="string" name="config_wifi_tether_enable"/> + </policy> + </overlayable> +</resources> diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml index ca866a946ea2..792bce9fc334 100644 --- a/packages/Tethering/res/values/strings.xml +++ b/packages/Tethering/res/values/strings.xml @@ -1,4 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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> <!-- Shown when the device is tethered --> <!-- Strings for tethered notification title [CHAR LIMIT=200] --> @@ -9,8 +23,11 @@ <!-- This notification is shown when tethering has been disabled on a user's device. The device is managed by the user's employer. Tethering can't be turned on unless the IT administrator allows it. The noun "admin" is another reference for "IT administrator." --> - <!-- Strings for tether disabling notification title [CHAR LIMIT=200] --> + <!-- Strings for tether disabling notification title [CHAR LIMIT=200] --> <string name="disable_tether_notification_title">Tethering is disabled</string> - <!-- Strings for tether disabling notification message [CHAR LIMIT=200] --> + <!-- Strings for tether disabling notification message [CHAR LIMIT=200] --> <string name="disable_tether_notification_message">Contact your admin for details</string> + + <!-- Strings for tether notification channel name [CHAR LIMIT=200] --> + <string name="notification_channel_tethering_status">Hotspot & tethering status</string> </resources>
\ No newline at end of file diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index d6abfb922a9c..038d7ae72a1a 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -50,6 +50,7 @@ import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANG import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; @@ -106,8 +107,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; @@ -663,19 +662,19 @@ public class Tethering { if (usbTethered) { if (wifiTethered || bluetoothTethered) { - showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL); + showTetheredNotification(R.drawable.stat_sys_tether_general); } else { - showTetheredNotification(SystemMessage.NOTE_TETHER_USB); + showTetheredNotification(R.drawable.stat_sys_tether_usb); } } else if (wifiTethered) { if (bluetoothTethered) { - showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL); + showTetheredNotification(R.drawable.stat_sys_tether_general); } else { /* We now have a status bar icon for WifiTethering, so drop the notification */ clearTetheredNotification(); } } else if (bluetoothTethered) { - showTetheredNotification(SystemMessage.NOTE_TETHER_BLUETOOTH); + showTetheredNotification(R.drawable.stat_sys_tether_bluetooth); } else { clearTetheredNotification(); } @@ -688,30 +687,22 @@ public class Tethering { @VisibleForTesting protected void showTetheredNotification(int id, boolean tetheringOn) { NotificationManager notificationManager = - (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0) + .getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) { return; } - int icon = 0; - switch(id) { - case SystemMessage.NOTE_TETHER_USB: - icon = R.drawable.stat_sys_tether_usb; - break; - case SystemMessage.NOTE_TETHER_BLUETOOTH: - icon = R.drawable.stat_sys_tether_bluetooth; - break; - case SystemMessage.NOTE_TETHER_GENERAL: - default: - icon = R.drawable.stat_sys_tether_general; - break; - } + final NotificationChannel channel = new NotificationChannel( + "TETHERING_STATUS", + mContext.getResources().getString(R.string.notification_channel_tethering_status), + NotificationManager.IMPORTANCE_LOW); + notificationManager.createNotificationChannel(channel); if (mLastNotificationId != 0) { - if (mLastNotificationId == icon) { + if (mLastNotificationId == id) { return; } - notificationManager.cancelAsUser(null, mLastNotificationId, - UserHandle.ALL); + notificationManager.cancel(null, mLastNotificationId); mLastNotificationId = 0; } @@ -719,8 +710,8 @@ public class Tethering { intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, - null, UserHandle.CURRENT); + PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null); Resources r = mContext.getResources(); final CharSequence title; @@ -735,32 +726,31 @@ public class Tethering { } if (mTetheredNotificationBuilder == null) { - mTetheredNotificationBuilder = new Notification.Builder(mContext, - SystemNotificationChannels.NETWORK_STATUS); + mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId()); mTetheredNotificationBuilder.setWhen(0) .setOngoing(true) .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) + android.R.color.system_notification_accent_color)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setCategory(Notification.CATEGORY_STATUS); } - mTetheredNotificationBuilder.setSmallIcon(icon) + mTetheredNotificationBuilder.setSmallIcon(id) .setContentTitle(title) .setContentText(message) .setContentIntent(pi); mLastNotificationId = id; - notificationManager.notifyAsUser(null, mLastNotificationId, - mTetheredNotificationBuilder.buildInto(new Notification()), UserHandle.ALL); + notificationManager.notify(null, mLastNotificationId, + mTetheredNotificationBuilder.buildInto(new Notification())); } @VisibleForTesting protected void clearTetheredNotification() { NotificationManager notificationManager = - (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0) + .getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null && mLastNotificationId != 0) { - notificationManager.cancelAsUser(null, mLastNotificationId, - UserHandle.ALL); + notificationManager.cancel(null, mLastNotificationId); mLastNotificationId = 0; } } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 397ba8ada551..dbe789288c6f 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -21,7 +21,7 @@ import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static com.android.internal.R.array.config_mobile_hotspot_provision_app; import static com.android.internal.R.array.config_tether_bluetooth_regexs; @@ -33,13 +33,13 @@ import static com.android.internal.R.array.config_tether_wifi_regexs; import static com.android.internal.R.bool.config_tether_upstream_automatic; import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period; import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui; +import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; -import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.net.TetheringConfigurationParcel; import android.net.util.SharedLog; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -84,6 +84,12 @@ public class TetheringConfiguration { private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"}; + /** + * Use the old dnsmasq DHCP server for tethering instead of the framework implementation. + */ + public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = + "tether_enable_legacy_dhcp_server"; + public final String[] tetherableUsbRegexs; public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; @@ -122,7 +128,7 @@ public class TetheringConfiguration { legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); - enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx); + enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app); provisioningAppNoUi = getProvisioningAppNoUi(res); @@ -332,10 +338,14 @@ public class TetheringConfiguration { } } - private static boolean getEnableLegacyDhcpServer(Context ctx) { - final ContentResolver cr = ctx.getContentResolver(); - final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); - return intVal != 0; + private boolean getEnableLegacyDhcpServer(final Resources res) { + return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server) + || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER); + } + + @VisibleForTesting + protected boolean getDeviceConfigBoolean(final String name) { + return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */); } private Resources getResources(Context ctx, int subId) { diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index 5692a6fc5c80..2875f71e5ed2 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -23,6 +23,8 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -215,19 +217,28 @@ public class UpstreamNetworkMonitor { mLog.e("registerMobileNetworkRequest() already registered"); return; } - // The following use of the legacy type system cannot be removed until - // after upstream selection no longer finds networks by legacy type. - // See also http://b/34364553 . - final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; - final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder() - .setCapabilities(networkCapabilitiesForType(legacyType)) - .build(); + final NetworkRequest mobileUpstreamRequest; + if (mDunRequired) { + mobileUpstreamRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_DUN) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .addTransportType(TRANSPORT_CELLULAR).build(); + } else { + mobileUpstreamRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .addTransportType(TRANSPORT_CELLULAR).build(); + } // The existing default network and DUN callbacks will be notified. // Therefore, to avoid duplicate notifications, we only register a no-op. mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST); + // The following use of the legacy type system cannot be removed until + // upstream selection no longer finds networks by legacy type. + // See also http://b/34364553 . + final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; + // TODO: Change the timeout from 0 (no onUnavailable callback) to some // moderate callback timeout. This might be useful for updating some UI. // Additionally, we log a message to aid in any subsequent debugging. diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 66eba9ae3b7a..79bba7f6e663 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -22,10 +22,12 @@ import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; +import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -39,7 +41,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.net.util.SharedLog; @@ -49,9 +50,8 @@ import android.os.PersistableBundle; import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.telephony.CarrierConfigManager; -import android.test.mock.MockContentResolver; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -60,7 +60,6 @@ import com.android.internal.R; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.internal.util.test.FakeSettingsProvider; import org.junit.After; import org.junit.Before; @@ -94,7 +93,6 @@ public final class EntitlementManagerTest { private final PersistableBundle mCarrierConfig = new PersistableBundle(); private final TestLooper mLooper = new TestLooper(); private Context mMockContext; - private MockContentResolver mContentResolver; private TestStateMachine mSM; private WrappedEntitlementManager mEnMgr; @@ -110,11 +108,6 @@ public final class EntitlementManagerTest { public Resources getResources() { return mResources; } - - @Override - public ContentResolver getContentResolver() { - return mContentResolver; - } } public class WrappedEntitlementManager extends EntitlementManager { @@ -151,13 +144,17 @@ public final class EntitlementManagerTest { MockitoAnnotations.initMocks(this); mMockingSession = mockitoSession() .initMocks(this) - .spyStatic(SystemProperties.class) + .mockStatic(SystemProperties.class) + .mockStatic(DeviceConfig.class) .strictness(Strictness.WARN) .startMocking(); // Don't disable tethering provisioning unless requested. doReturn(false).when( () -> SystemProperties.getBoolean( eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean())); + doReturn(false).when( + () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); @@ -169,10 +166,9 @@ public final class EntitlementManagerTest { .thenReturn(new String[0]); when(mResources.getIntArray(R.array.config_tether_upstream_types)) .thenReturn(new int[0]); + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); when(mLog.forSubComponent(anyString())).thenReturn(mLog); - mContentResolver = new MockContentResolver(); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); mMockContext = new MockContext(mContext); mSM = new TestStateMachine(); mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 7799da4b94a7..ef97ad418245 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -21,40 +21,44 @@ import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.internal.R.array.config_mobile_hotspot_provision_app; import static com.android.internal.R.array.config_tether_bluetooth_regexs; import static com.android.internal.R.array.config_tether_dhcp_range; import static com.android.internal.R.array.config_tether_upstream_types; import static com.android.internal.R.array.config_tether_usb_regexs; import static com.android.internal.R.array.config_tether_wifi_regexs; +import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; -import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.net.util.SharedLog; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.telephony.TelephonyManager; -import android.test.mock.MockContentResolver; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.internal.util.test.FakeSettingsProvider; +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 org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.util.Arrays; import java.util.Iterator; @@ -69,9 +73,10 @@ public class TetheringConfigurationTest { @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @Mock private Resources mResourcesForSubId; - private MockContentResolver mContentResolver; private Context mMockContext; private boolean mHasTelephonyManager; + private boolean mEnableLegacyDhcpServer; + private MockitoSession mMockingSession; private class MockTetheringConfiguration extends TetheringConfiguration { MockTetheringConfiguration(Context ctx, SharedLog log, int id) { @@ -101,16 +106,20 @@ public class TetheringConfigurationTest { } return super.getSystemService(name); } - - @Override - public ContentResolver getContentResolver() { - return mContentResolver; - } } @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); + // TODO: use a dependencies class instead of mock statics. + mMockingSession = mockitoSession() + .initMocks(this) + .mockStatic(DeviceConfig.class) + .strictness(Strictness.WARN) + .startMocking(); + doReturn(false).when( + () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]); when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]); when(mResources.getStringArray(config_tether_wifi_regexs)) @@ -119,10 +128,15 @@ public class TetheringConfigurationTest { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]); when(mResources.getStringArray(config_mobile_hotspot_provision_app)) .thenReturn(new String[0]); - mContentResolver = new MockContentResolver(); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); mHasTelephonyManager = true; mMockContext = new MockContext(mContext); + mEnableLegacyDhcpServer = false; + } + + @After + public void tearDown() throws Exception { + mMockingSession.finishMocking(); } private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) { @@ -268,19 +282,35 @@ public class TetheringConfigurationTest { @Test public void testNewDhcpServerDisabled() { - Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(cfg.enableLegacyDhcpServer); + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true); + doReturn(false).when( + () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + + final TetheringConfiguration enableByRes = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(enableByRes.enableLegacyDhcpServer); + + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + doReturn(true).when( + () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + + final TetheringConfiguration enableByDevConfig = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(enableByDevConfig.enableLegacyDhcpServer); } @Test public void testNewDhcpServerEnabled() { - Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + doReturn(false).when( + () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + + final TetheringConfiguration cfg = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertFalse(cfg.enableLegacyDhcpServer); } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 7af48a89d87c..e1fe3bfda4a8 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -35,9 +35,10 @@ import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; -import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -224,6 +225,11 @@ public class TetheringTest { if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; return super.getSystemServiceName(serviceClass); } + + @Override + public Context createContextAsUser(UserHandle user, int flags) { + return mContext; + } } public class MockIpServerDependencies extends IpServer.Dependencies { @@ -265,6 +271,11 @@ public class TetheringTest { } @Override + protected boolean getDeviceConfigBoolean(final String name) { + return false; + } + + @Override protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { return mResources; } @@ -423,6 +434,7 @@ public class TetheringTest { .thenReturn(new int[0]); when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) .thenReturn(false); + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); @@ -432,9 +444,9 @@ public class TetheringTest { .thenReturn(true); mServiceContext = new TestContext(mContext); + when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -686,7 +698,7 @@ public class TetheringTest { @Test public void workingMobileUsbTethering_IPv4LegacyDhcp() { - Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); + when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true); sendConfigurationChanged(); final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); runUsbTethering(upstreamState); diff --git a/proto/Android.bp b/proto/Android.bp index 65bccbb4aac8..7cf6ce740969 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -27,3 +27,8 @@ java_library_static { srcs: ["src/metrics_constants/metrics_constants.proto"], sdk_version: "system_current", } + +filegroup { + name: "system-messages-proto-src", + srcs: ["src/system_messages.proto"], +} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index cbff6bdcec77..37ac3ec6f105 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -35,6 +35,7 @@ import android.provider.Settings; import android.util.Slog; import android.view.Display; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -264,6 +265,24 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } @Override + public boolean switchToInputMethod(String imeId) { + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return false; + } + } + final boolean result; + final int callingUserId = UserHandle.getCallingUserId(); + final long identity = Binder.clearCallingIdentity(); + try { + result = InputMethodManagerInternal.get().switchToInputMethod(imeId, callingUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + return result; + } + + @Override public boolean isAccessibilityButtonAvailable() { synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 7dd4a7089954..7a8a112fae40 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -297,6 +297,11 @@ class UiAutomationManager { } @Override + public boolean switchToInputMethod(String imeId) { + return false; + } + + @Override public boolean isAccessibilityButtonAvailable() { return false; } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 03d9626cab91..5405fc74dfb6 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -331,8 +331,8 @@ public final class AutofillManagerService } @Override // from SystemService - public boolean isSupported(UserInfo userInfo) { - return userInfo.isFull() || userInfo.isManagedProfile(); + public boolean isSupportedUser(TargetUser user) { + return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); } @Override // from SystemService diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index d45a54e0ff28..b13bef2de151 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1406,10 +1406,7 @@ public class BackupManagerService extends IBackupManager.Stub { long oldId = Binder.clearCallingIdentity(); final int[] userIds; try { - userIds = - mContext - .getSystemService(UserManager.class) - .getProfileIds(callingUserId, false); + userIds = getUserManager().getProfileIds(callingUserId, false); } finally { Binder.restoreCallingIdentity(oldId); } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 27824afb8d46..8eca62a4932b 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -175,8 +175,8 @@ public final class ContentCaptureManagerService extends } @Override // from SystemService - public boolean isSupported(UserInfo userInfo) { - return userInfo.isFull() || userInfo.isManagedProfile(); + public boolean isSupportedUser(TargetUser user) { + return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); } @Override // from SystemService diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 76572d35d516..368416b0d4a1 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -41,6 +41,7 @@ import com.android.server.pm.PackageList; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; import java.util.List; import java.util.function.Consumer; @@ -478,10 +479,12 @@ public abstract class PackageManagerInternal { * will be disabled. Pass in null or an empty list to disable * all overlays. The order of the items is significant if several * overlays modify the same resource. + * @param outUpdatedPackageNames An output list that contains the package names of packages + * affected by the update of enabled overlays. * @return true if all packages names were known by the package manager, false otherwise */ public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName, - List<String> overlayPackageNames); + List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames); /** * Resolves an activity intent, allowing instant apps to be resolved. diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 73b6c7a570ed..0f2fb9252c29 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -208,6 +208,7 @@ class AlarmManagerService extends SystemService { AppWakeupHistory mAppWakeupHistory; ClockReceiver mClockReceiver; final DeliveryTracker mDeliveryTracker = new DeliveryTracker(); + IBinder.DeathRecipient mListenerDeathRecipient; Intent mTimeTickIntent; IAlarmListener mTimeTickTrigger; PendingIntent mDateChangeSender; @@ -1447,6 +1448,18 @@ class AlarmManagerService extends SystemService { public void onStart() { mInjector.init(); + mListenerDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + } + + @Override + public void binderDied(IBinder who) { + final IAlarmListener listener = IAlarmListener.Stub.asInterface(who); + removeImpl(null, listener); + } + }; + synchronized (mLock) { mHandler = new AlarmHandler(); mConstants = new Constants(mHandler); @@ -1653,6 +1666,15 @@ class AlarmManagerService extends SystemService { return; } + if (directReceiver != null) { + try { + directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0); + } catch (RemoteException e) { + Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag); + return; + } + } + // Sanity check the window length. This will catch people mistakenly // trying to pass an end-of-window timestamp rather than a duration. if (windowLength > AlarmManager.INTERVAL_HALF_DAY) { @@ -2079,8 +2101,9 @@ class AlarmManagerService extends SystemService { @Override public long currentNetworkTimeMillis() { final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext()); - if (time.hasCache()) { - return time.currentTimeMillis(); + NtpTrustedTime.TimeResult ntpResult = time.getCachedTimeResult(); + if (ntpResult != null) { + return ntpResult.currentTimeMillis(); } else { throw new ParcelableException(new DateTimeException("Missing NTP fix")); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c2e32d332f50..f4962e53662c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -84,12 +84,12 @@ import android.net.MatchAllNetworkSpecifier; import android.net.NattSocketKeepalive; import android.net.Network; import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkConfig; import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; -import android.net.NetworkMisc; import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; import android.net.NetworkProvider; @@ -364,10 +364,10 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_PROXY_HAS_CHANGED = 16; /** - * used internally when registering NetworkFactories - * obj = NetworkFactoryInfo + * used internally when registering NetworkProviders + * obj = NetworkProviderInfo */ - private static final int EVENT_REGISTER_NETWORK_FACTORY = 17; + private static final int EVENT_REGISTER_NETWORK_PROVIDER = 17; /** * used internally when registering NetworkAgents @@ -403,10 +403,10 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_RELEASE_NETWORK_REQUEST = 22; /** - * used internally when registering NetworkFactories + * used internally when registering NetworkProviders * obj = Messenger */ - private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23; + private static final int EVENT_UNREGISTER_NETWORK_PROVIDER = 23; /** * used internally to expire a wakelock when transitioning @@ -2394,9 +2394,9 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } - pw.print("NetworkFactories for:"); - for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - pw.print(" " + nfi.name); + pw.print("NetworkProviders for:"); + for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) { + pw.print(" " + npi.name); } pw.println(); pw.println(); @@ -2631,8 +2631,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai.everConnected) { loge("ERROR: cannot call explicitlySelected on already-connected network"); } - nai.networkMisc.explicitlySelected = toBool(msg.arg1); - nai.networkMisc.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2); + nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1); + nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2); // Mark the network as temporarily accepting partial connectivity so that it // will be validated (and possibly become default) even if it only provides // partial internet access. Note that if user connects to partial connectivity @@ -2640,7 +2640,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // out of wifi coverage) and if the same wifi is available again, the device // will auto connect to this wifi even though the wifi has "no internet". // TODO: Evaluate using a separate setting in IpMemoryStore. - nai.networkMisc.acceptPartialConnectivity = toBool(msg.arg2); + nai.networkAgentConfig.acceptPartialConnectivity = toBool(msg.arg2); break; } case NetworkAgent.EVENT_SOCKET_KEEPALIVE: { @@ -2672,10 +2672,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Only show the notification when the private DNS is broken and the // PRIVATE_DNS_BROKEN notification hasn't shown since last valid. - if (privateDnsBroken && !nai.networkMisc.hasShownBroken) { + if (privateDnsBroken && !nai.networkAgentConfig.hasShownBroken) { showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN); } - nai.networkMisc.hasShownBroken = privateDnsBroken; + nai.networkAgentConfig.hasShownBroken = privateDnsBroken; } else if (nai.networkCapabilities.isPrivateDnsBroken()) { // If probePrivateDnsCompleted is false but nai.networkCapabilities says // private DNS is broken, it means this network is being reevaluated. @@ -2685,7 +2685,7 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.networkCapabilities.setPrivateDnsBroken(false); final int oldScore = nai.getCurrentScore(); updateCapabilities(oldScore, nai, nai.networkCapabilities); - nai.networkMisc.hasShownBroken = false; + nai.networkAgentConfig.hasShownBroken = false; } break; } @@ -2727,7 +2727,7 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.lastValidated = valid; nai.everValidated |= valid; updateCapabilities(oldScore, nai, nai.networkCapabilities); - // If score has changed, rebroadcast to NetworkFactories. b/17726566 + // If score has changed, rebroadcast to NetworkProviders. b/17726566 if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); if (valid) { handleFreshlyValidatedNetwork(nai); @@ -2744,7 +2744,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // If network becomes valid, the hasShownBroken should be reset for // that network so that the notification will be fired when the private // DNS is broken again. - nai.networkMisc.hasShownBroken = false; + nai.networkAgentConfig.hasShownBroken = false; } } else if (partialConnectivityChanged) { updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); @@ -2803,9 +2803,10 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor"); break; } - if (!nai.networkMisc.provisioningNotificationDisabled) { + if (!nai.networkAgentConfig.provisioningNotificationDisabled) { mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null, - (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected); + (PendingIntent) msg.obj, + nai.networkAgentConfig.explicitlySelected); } } break; @@ -2842,6 +2843,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } + // TODO: delete when direct use of registerNetworkFactory is no longer supported. private boolean maybeHandleNetworkFactoryMessage(Message msg) { switch (msg.what) { default: @@ -3031,16 +3033,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleAsyncChannelHalfConnect(Message msg) { ensureRunningOnConnectivityServiceThread(); final AsyncChannel ac = (AsyncChannel) msg.obj; - if (mNetworkFactoryInfos.containsKey(msg.replyTo)) { + if (mNetworkProviderInfos.containsKey(msg.replyTo)) { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { if (VDBG) log("NetworkFactory connected"); // Finish setting up the full connection - NetworkFactoryInfo nfi = mNetworkFactoryInfos.get(msg.replyTo); - nfi.completeConnection(); - sendAllRequestsToFactory(nfi); + NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo); + npi.completeConnection(); + sendAllRequestsToProvider(npi); } else { loge("Error connecting NetworkFactory"); - mNetworkFactoryInfos.remove(msg.obj); + mNetworkProviderInfos.remove(msg.obj); } } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { @@ -3072,8 +3074,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai != null) { disconnectAndDestroyNetwork(nai); } else { - NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo); - if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name); + NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo); + if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name); } } @@ -3156,7 +3158,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // ip[6]tables to flush routes and remove the incoming packet mark rule, so do it // after we've rematched networks with requests which should make a potential // fallback network the default or requested a new network from the - // NetworkFactories, so network traffic isn't interrupted for an unnecessarily + // NetworkProviders, so network traffic isn't interrupted for an unnecessarily // long time. destroyNativeNetwork(nai); mDnsManager.removeNetwork(nai.network); @@ -3169,8 +3171,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // This should never fail. Specifying an already in use NetID will cause failure. if (networkAgent.isVPN()) { mNetd.networkCreateVpn(networkAgent.network.netId, - (networkAgent.networkMisc == null - || !networkAgent.networkMisc.allowBypass)); + (networkAgent.networkAgentConfig == null + || !networkAgent.networkAgentConfig.allowBypass)); } else { mNetd.networkCreatePhysical(networkAgent.network.netId, getNetworkPermission(networkAgent.networkCapabilities)); @@ -3419,8 +3421,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.cancelRequest(nri.request); + for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) { + npi.cancelRequest(nri.request); } } else { // listens don't have a singular affectedNetwork. Check all networks to see @@ -3470,16 +3472,16 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } - if (!nai.networkMisc.explicitlySelected) { + if (!nai.networkAgentConfig.explicitlySelected) { Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network"); } - if (accept != nai.networkMisc.acceptUnvalidated) { - nai.networkMisc.acceptUnvalidated = accept; + if (accept != nai.networkAgentConfig.acceptUnvalidated) { + nai.networkAgentConfig.acceptUnvalidated = accept; // If network becomes partial connectivity and user already accepted to use this // network, we should respect the user's option and don't need to popup the // PARTIAL_CONNECTIVITY notification to user again. - nai.networkMisc.acceptPartialConnectivity = accept; + nai.networkAgentConfig.acceptPartialConnectivity = accept; rematchAllNetworksAndRequests(); sendUpdatedScoreToFactories(nai); } @@ -3516,8 +3518,8 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } - if (accept != nai.networkMisc.acceptPartialConnectivity) { - nai.networkMisc.acceptPartialConnectivity = accept; + if (accept != nai.networkAgentConfig.acceptPartialConnectivity) { + nai.networkAgentConfig.acceptPartialConnectivity = accept; } // TODO: Use the current design or save the user choice into IpMemoryStore. @@ -3615,14 +3617,29 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceSettingsPermission(); } + final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork); + if (nm == null) return; + nm.notifyCaptivePortalAppFinished(response); + } + + @Override + public void appRequest(final int request) { + final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork); + if (nm == null) return; + + if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) { + nm.forceReevaluation(Binder.getCallingUid()); + } + } + + @Nullable + private NetworkMonitorManager getNetworkMonitorManager(final Network network) { // getNetworkAgentInfoForNetwork is thread-safe - final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork); - if (nai == null) return; + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + if (nai == null) return null; // nai.networkMonitor() is thread-safe - final NetworkMonitorManager nm = nai.networkMonitor(); - if (nm == null) return; - nm.notifyCaptivePortalAppFinished(response); + return nai.networkMonitor(); } @Override @@ -3727,7 +3744,7 @@ public class ConnectivityService extends IConnectivityManager.Stub action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY; // Don't bother the user with a high-priority notification if the network was not // explicitly selected by the user. - highPriority = nai.networkMisc.explicitlySelected; + highPriority = nai.networkAgentConfig.explicitlySelected; break; default: Slog.wtf(TAG, "Unknown notification type " + type); @@ -3760,14 +3777,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // automatically connects to a network that has partial Internet access, the user will // always be able to use it, either because they've already chosen "don't ask again" or // because we have prompt them. - if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) { + if (nai.partialConnectivity && !nai.networkAgentConfig.acceptPartialConnectivity) { return true; } // If a network has no Internet access, only prompt if the network was explicitly selected // and if the user has not already told us to use the network regardless of whether it // validated or not. - if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) { + if (nai.networkAgentConfig.explicitlySelected + && !nai.networkAgentConfig.acceptUnvalidated) { return true; } @@ -3855,12 +3873,12 @@ public class ConnectivityService extends IConnectivityManager.Stub handleApplyDefaultProxy((ProxyInfo)msg.obj); break; } - case EVENT_REGISTER_NETWORK_FACTORY: { - handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj); + case EVENT_REGISTER_NETWORK_PROVIDER: { + handleRegisterNetworkProvider((NetworkProviderInfo) msg.obj); break; } - case EVENT_UNREGISTER_NETWORK_FACTORY: { - handleUnregisterNetworkFactory((Messenger)msg.obj); + case EVENT_UNREGISTER_NETWORK_PROVIDER: { + handleUnregisterNetworkProvider((Messenger) msg.obj); break; } case EVENT_REGISTER_NETWORK_AGENT: { @@ -4905,7 +4923,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } }; - private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos = new HashMap<>(); + private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>(); private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>(); private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; @@ -4913,18 +4931,18 @@ public class ConnectivityService extends IConnectivityManager.Stub @GuardedBy("mUidToNetworkRequestCount") private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray(); - private static class NetworkFactoryInfo { + private static class NetworkProviderInfo { public final String name; public final Messenger messenger; private final AsyncChannel mAsyncChannel; private final IBinder.DeathRecipient mDeathRecipient; - public final int factorySerialNumber; + public final int providerId; - NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel, - int factorySerialNumber, IBinder.DeathRecipient deathRecipient) { + NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel, + int providerId, IBinder.DeathRecipient deathRecipient) { this.name = name; this.messenger = messenger; - this.factorySerialNumber = factorySerialNumber; + this.providerId = providerId; mAsyncChannel = asyncChannel; mDeathRecipient = deathRecipient; @@ -4942,17 +4960,17 @@ public class ConnectivityService extends IConnectivityManager.Stub messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj)); } catch (RemoteException e) { // Remote process died. Ignore; the death recipient will remove this - // NetworkFactoryInfo from mNetworkFactoryInfos. + // NetworkProviderInfo from mNetworkProviderInfos. } } - void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) { + void requestNetwork(NetworkRequest request, int score, int servingProviderId) { if (isLegacyNetworkFactory()) { mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, - servingSerialNumber, request); + servingProviderId, request); } else { sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score, - servingSerialNumber, request); + servingProviderId, request); } } @@ -5365,45 +5383,45 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public int registerNetworkFactory(Messenger messenger, String name) { enforceNetworkFactoryPermission(); - NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(), + NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(), nextNetworkProviderId(), null /* deathRecipient */); - mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi)); - return nfi.factorySerialNumber; + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi)); + return npi.providerId; } - private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) { - if (mNetworkFactoryInfos.containsKey(nfi.messenger)) { + private void handleRegisterNetworkProvider(NetworkProviderInfo npi) { + if (mNetworkProviderInfos.containsKey(npi.messenger)) { // Avoid creating duplicates. even if an app makes a direct AIDL call. // This will never happen if an app calls ConnectivityManager#registerNetworkProvider, // as that will throw if a duplicate provider is registered. - Slog.e(TAG, "Attempt to register existing NetworkFactoryInfo " - + mNetworkFactoryInfos.get(nfi.messenger).name); + Slog.e(TAG, "Attempt to register existing NetworkProviderInfo " + + mNetworkProviderInfos.get(npi.messenger).name); return; } - if (DBG) log("Got NetworkFactory Messenger for " + nfi.name); - mNetworkFactoryInfos.put(nfi.messenger, nfi); - nfi.connect(mContext, mTrackerHandler); - if (!nfi.isLegacyNetworkFactory()) { + if (DBG) log("Got NetworkProvider Messenger for " + npi.name); + mNetworkProviderInfos.put(npi.messenger, npi); + npi.connect(mContext, mTrackerHandler); + if (!npi.isLegacyNetworkFactory()) { // Legacy NetworkFactories get their requests when their AsyncChannel connects. - sendAllRequestsToFactory(nfi); + sendAllRequestsToProvider(npi); } } @Override public int registerNetworkProvider(Messenger messenger, String name) { enforceNetworkFactoryPermission(); - NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, + NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, null /* asyncChannel */, nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger)); - mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi)); - return nfi.factorySerialNumber; + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi)); + return npi.providerId; } @Override public void unregisterNetworkProvider(Messenger messenger) { enforceNetworkFactoryPermission(); - mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger)); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger)); } @Override @@ -5411,13 +5429,13 @@ public class ConnectivityService extends IConnectivityManager.Stub unregisterNetworkProvider(messenger); } - private void handleUnregisterNetworkFactory(Messenger messenger) { - NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger); - if (nfi == null) { - loge("Failed to find Messenger in unregisterNetworkFactory"); + private void handleUnregisterNetworkProvider(Messenger messenger) { + NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger); + if (npi == null) { + loge("Failed to find Messenger in unregisterNetworkProvider"); return; } - if (DBG) log("unregisterNetworkFactory for " + nfi.name); + if (DBG) log("unregisterNetworkProvider for " + npi.name); } @Override @@ -5489,9 +5507,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // tree. public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkMisc networkMisc) { + int currentScore, NetworkAgentConfig networkAgentConfig) { return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities, - currentScore, networkMisc, NetworkFactory.SerialNumber.NONE); + currentScore, networkAgentConfig, NetworkProvider.ID_NONE); } /** @@ -5506,12 +5524,12 @@ public class ConnectivityService extends IConnectivityManager.Stub * later : see {@link #updateCapabilities}. * @param currentScore the initial score of the network. See * {@link NetworkAgentInfo#getCurrentScore}. - * @param networkMisc metadata about the network. This is never updated. - * @param factorySerialNumber the serial number of the factory owning this NetworkAgent. + * @param networkAgentConfig metadata about the network. This is never updated. + * @param providerId the ID of the provider owning this NetworkAgent. */ public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkMisc networkMisc, int factorySerialNumber) { + int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { enforceNetworkFactoryPermission(); LinkProperties lp = new LinkProperties(linkProperties); @@ -5523,8 +5541,8 @@ public class ConnectivityService extends IConnectivityManager.Stub ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore); final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, - ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, - mDnsResolver, mNMS, factorySerialNumber); + ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this, + mNetd, mDnsResolver, mNMS, providerId); // Make sure the network capabilities reflect what the agent info says. nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc)); final String extraInfo = networkInfo.getExtraInfo(); @@ -5803,7 +5821,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Once a NetworkAgent is connected, complain if some immutable capabilities are removed. // Don't complain for VPNs since they're not driven by requests and there is no risk of // causing a connect/teardown loop. - // TODO: remove this altogether and make it the responsibility of the NetworkFactories to + // TODO: remove this altogether and make it the responsibility of the NetworkProviders to // avoid connect/teardown loops. if (nai.everConnected && !nai.isVPN() && @@ -5945,7 +5963,7 @@ public class ConnectivityService extends IConnectivityManager.Stub LinkProperties lp) { if (nc == null || lp == null) return false; return nai.isVPN() - && !nai.networkMisc.allowBypass + && !nai.networkAgentConfig.allowBypass && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID && lp.getInterfaceName() != null && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()); @@ -6043,13 +6061,13 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG || DDBG){ log("sending new Min Network Score(" + score + "): " + networkRequest.toString()); } - for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.requestNetwork(networkRequest, score, serial); + for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) { + npi.requestNetwork(networkRequest, score, serial); } } /** Sends all current NetworkRequests to the specified factory. */ - private void sendAllRequestsToFactory(NetworkFactoryInfo nfi) { + private void sendAllRequestsToProvider(NetworkProviderInfo npi) { ensureRunningOnConnectivityServiceThread(); for (NetworkRequestInfo nri : mNetworkRequests.values()) { if (nri.request.isListen()) continue; @@ -6061,9 +6079,9 @@ public class ConnectivityService extends IConnectivityManager.Stub serial = nai.factorySerialNumber; } else { score = 0; - serial = NetworkFactory.SerialNumber.NONE; + serial = NetworkProvider.ID_NONE; } - nfi.requestNetwork(nri.request, score, serial); + npi.requestNetwork(nri.request, score, serial); } } @@ -6344,11 +6362,11 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request); } addedRequests.add(nri); - // Tell NetworkFactories about the new score, so they can stop + // Tell NetworkProviders about the new score, so they can stop // trying to connect if they know they cannot match it. // TODO - this could get expensive if we have a lot of requests for this // network. Think about if there is a way to reduce this. Push - // netid->request mapping to each factory? + // netid->request mapping to each provider? sendUpdatedScoreToFactories(nri.request, newSatisfier); if (isDefaultRequest(nri)) { isNewDefault = true; @@ -6377,7 +6395,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " + newNetwork.name() + - " without updating mSatisfier or factories!"); + " without updating mSatisfier or providers!"); } // TODO: Technically, sending CALLBACK_LOST here is // incorrect if there is a replacement network currently @@ -6636,7 +6654,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // command must be sent after updating LinkProperties to maximize chances of // NetworkMonitor seeing the correct LinkProperties when starting. // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call. - if (networkAgent.networkMisc.acceptPartialConnectivity) { + if (networkAgent.networkAgentConfig.acceptPartialConnectivity) { networkAgent.networkMonitor().setAcceptPartialConnectivity(); } networkAgent.networkMonitor().notifyNetworkConnected( diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java index d20936c2d217..7894788cb0b6 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java @@ -154,17 +154,20 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU private void onPollNetworkTimeUnderWakeLock(int event) { // Force an NTP fix when outdated - if (mTime.getCacheAge() >= mPollingIntervalMs) { + NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult(); + if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) { if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh"); mTime.forceRefresh(); + cachedNtpResult = mTime.getCachedTimeResult(); } - if (mTime.getCacheAge() < mPollingIntervalMs) { + if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) { // Obtained fresh fix; schedule next normal update resetAlarm(mPollingIntervalMs); // Suggest the time to the time detector. It may choose use it to set the system clock. - TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal(); + TimestampedValue<Long> timeSignal = new TimestampedValue<>( + cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis()); NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal); timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event); mTimeDetector.suggestNetworkTime(timeSuggestion); @@ -275,8 +278,11 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU TimeUtils.formatDuration(mPollingIntervalShorterMs, pw); pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax); pw.println("\nTryAgainCounter: " + mTryAgainCounter); - pw.println("NTP cache age: " + mTime.getCacheAge()); - pw.println("NTP cache certainty: " + mTime.getCacheCertainty()); + NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult(); + pw.println("NTP cache result: " + ntpResult); + if (ntpResult != null) { + pw.println("NTP result age: " + ntpResult.getAgeMillis()); + } pw.println(); } } diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java index 190fff1f669c..21fa9f9a9401 100644 --- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java +++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java @@ -46,4 +46,7 @@ public interface PersistentDataBlockManagerInternal { /** Update the OEM unlock enabled bit, bypassing user restriction checks. */ void forceOemUnlockEnabled(boolean enabled); + + /** Retrieves the UID that can access the persistent data partition. */ + int getAllowedUid(); } diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index 73c852083cfd..00d8b0f1bed4 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -680,6 +680,11 @@ public class PersistentDataBlockService extends SystemService { writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size)); } + @Override + public int getAllowedUid() { + return mAllowedUid; + } + private void writeInternal(byte[] data, long offset, int dataLength) { checkArgument(data == null || data.length > 0, "data must be null or non-empty"); checkArgument( diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index c5409f85bde3..f46b9ae3e8fb 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -18,18 +18,23 @@ package com.android.server; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityThread; import android.content.Context; import android.content.pm.UserInfo; import android.os.IBinder; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.UserManager; import com.android.server.pm.UserManagerService; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -57,15 +62,17 @@ import java.util.List; * * {@hide} */ +//@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER) public abstract class SystemService { + /** @hide */ // TODO(b/133242016) STOPSHIP: change to false before R ships protected static final boolean DEBUG_USER = true; /* - * Boot Phases + * The earliest boot phase the system send to system services on boot. */ - public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency? + public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; /** * After receiving this boot phase, services can obtain lock settings data. @@ -98,13 +105,70 @@ public abstract class SystemService { * After receiving this boot phase, services can allow user interaction with the device. * This phase occurs when boot has completed and the home application has started. * System services may prefer to listen to this phase rather than registering a - * broadcast receiver for ACTION_BOOT_COMPLETED to reduce overall latency. + * broadcast receiver for {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED} + * to reduce overall latency. */ public static final int PHASE_BOOT_COMPLETED = 1000; + /** @hide */ + @IntDef(flag = true, prefix = { "PHASE_" }, value = { + PHASE_WAIT_FOR_DEFAULT_DISPLAY, + PHASE_LOCK_SETTINGS_READY, + PHASE_SYSTEM_SERVICES_READY, + PHASE_DEVICE_SPECIFIC_SERVICES_READY, + PHASE_ACTIVITY_MANAGER_READY, + PHASE_THIRD_PARTY_APPS_CAN_START, + PHASE_BOOT_COMPLETED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BootPhase {} + private final Context mContext; /** + * Class representing user in question in the lifecycle callbacks. + * @hide + */ + //@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER) + public static final class TargetUser { + @NonNull + private final UserInfo mUserInfo; + + /** @hide */ + public TargetUser(@NonNull UserInfo userInfo) { + mUserInfo = userInfo; + } + + /** + * @return The information about the user. <b>NOTE: </b> this is a "live" object + * referenced by {@link UserManagerService} and hence should not be modified. + * + * @hide + */ + @NonNull + public UserInfo getUserInfo() { + return mUserInfo; + } + + /** + * @return the target {@link UserHandle}. + */ + @NonNull + public UserHandle getUserHandle() { + return mUserInfo.getUserHandle(); + } + + /** + * @return the integer user id + * + * @hide + */ + public int getUserIdentifier() { + return mUserInfo.id; + } + } + + /** * Initializes the system service. * <p> * Subclasses must define a single argument constructor that accepts the context @@ -113,13 +177,14 @@ public abstract class SystemService { * * @param context The system server context. */ - public SystemService(Context context) { + public SystemService(@NonNull Context context) { mContext = context; } /** * Gets the system context. */ + @NonNull public final Context getContext() { return mContext; } @@ -128,6 +193,8 @@ public abstract class SystemService { * Get the system UI context. This context is to be used for displaying UI. It is themable, * which means resources can be overridden at runtime. Do not use to retrieve properties that * configure the behavior of the device that is not UX related. + * + * @hide */ public final Context getUiContext() { // This has already been set up by the time any SystemServices are created. @@ -137,15 +204,16 @@ public abstract class SystemService { /** * Returns true if the system is running in safe mode. * TODO: we should define in which phase this becomes valid + * + * @hide */ public final boolean isSafeMode() { return getManager().isSafeMode(); } /** - * Called when the dependencies listed in the @Service class-annotation are available - * and after the chosen start phase. - * When this method returns, the service should be published. + * Called when the system service should publish a binder service using + * {@link #publishBinderService(String, IBinder).} */ public abstract void onStart(); @@ -155,7 +223,7 @@ public abstract class SystemService { * * @param phase The current boot phase. */ - public void onBootPhase(int phase) {} + public void onBootPhase(@BootPhase int phase) {} /** * Checks if the service should be available for the given user. @@ -163,12 +231,14 @@ public abstract class SystemService { * <p>By default returns {@code true}, but subclasses should extend for optimization, if they * don't support some types (like headless system user). */ - public boolean isSupported(@NonNull UserInfo userInfo) { + public boolean isSupportedUser(@NonNull TargetUser user) { return true; } /** - * Helper method used to dump which users are {@link #onStartUser(UserInfo) supported}. + * Helper method used to dump which users are {@link #onStartUser(TargetUser) supported}. + * + * @hide */ protected void dumpSupportedUsers(@NonNull PrintWriter pw, @NonNull String prefix) { final List<UserInfo> allUsers = UserManager.get(mContext).getUsers(); @@ -187,34 +257,59 @@ public abstract class SystemService { } /** - * @deprecated subclasses should extend {@link #onStartUser(UserInfo)} instead (which by default - * calls this method). + * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead + * (which by default calls this method). + * + * @hide */ @Deprecated public void onStartUser(@UserIdInt int userId) {} /** + * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead + * (which by default calls this method). + * + * @hide + */ + @Deprecated + public void onStartUser(@NonNull UserInfo userInfo) { + onStartUser(userInfo.id); + } + + /** * Called when a new user is starting, for system services to initialize any per-user * state they maintain for running users. * - * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this - * user. + * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * this user. * - * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object - * referenced by {@link UserManagerService} and hence should not be modified. + * @param user target user */ - public void onStartUser(@NonNull UserInfo userInfo) { - onStartUser(userInfo.id); + public void onStartUser(@NonNull TargetUser user) { + onStartUser(user.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onUnlockUser(UserInfo)} instead (which by + * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by * default calls this method). + * + * @hide */ @Deprecated public void onUnlockUser(@UserIdInt int userId) {} /** + * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by + * default calls this method). + * + * @hide + */ + @Deprecated + public void onUnlockUser(@NonNull UserInfo userInfo) { + onUnlockUser(userInfo.id); + } + + /** * Called when an existing user is in the process of being unlocked. This * means the credential-encrypted storage for that user is now available, * and encryption-aware component filtering is no longer in effect. @@ -226,90 +321,127 @@ public abstract class SystemService { * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of * these states. * <p> - * This method is only called when the service {@link #isSupported(UserInfo) supports} this - * user. + * This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * this user. * - * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object - * referenced by {@link UserManagerService} and hence should not be modified. + * @param user target user */ - public void onUnlockUser(@NonNull UserInfo userInfo) { - onUnlockUser(userInfo.id); + public void onUnlockUser(@NonNull TargetUser user) { + onUnlockUser(user.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onSwitchUser(UserInfo, UserInfo)} instead + * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead * (which by default calls this method). + * + * @hide */ @Deprecated - public void onSwitchUser(@UserIdInt int userId) {} + public void onSwitchUser(@UserIdInt int toUserId) {} + + /** + * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead + * (which by default calls this method). + * + * @hide + */ + @Deprecated + public void onSwitchUser(@Nullable UserInfo from, @NonNull UserInfo to) { + onSwitchUser(to.id); + } /** * Called when switching to a different foreground user, for system services that have * special behavior for whichever user is currently in the foreground. This is called * before any application processes are aware of the new user. * - * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} either - * of the users ({@code from} or {@code to}). + * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * either of the users ({@code from} or {@code to}). * * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects * referenced by {@link UserManagerService} and hence should not be modified. * - * @param from The information about the user being switched from. - * @param to The information about the user being switched from to. + * @param from the user switching from + * @param to the user switching to */ - public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) { - onSwitchUser(to.id); + public void onSwitchUser(@Nullable TargetUser from, @NonNull TargetUser to) { + onSwitchUser((from == null ? null : from.getUserInfo()), to.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onStopUser(UserInfo)} instead (which by default - * calls this method). + * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead + * (which by default calls this method). + * + * @hide */ @Deprecated public void onStopUser(@UserIdInt int userId) {} /** + * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead + * (which by default calls this method). + * + * @hide + */ + @Deprecated + public void onStopUser(@NonNull UserInfo user) { + onStopUser(user.id); + + } + + /** * Called when an existing user is stopping, for system services to finalize any per-user * state they maintain for running users. This is called prior to sending the SHUTDOWN * broadcast to the user; it is a good place to stop making use of any resources of that * user (such as binding to a service running in the user). * - * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this - * user. + * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * this user. * * <p>NOTE: This is the last callback where the callee may access the target user's CE storage. * - * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object - * referenced by {@link UserManagerService} and hence should not be modified. + * @param user target user */ - public void onStopUser(@NonNull UserInfo userInfo) { - onStopUser(userInfo.id); + public void onStopUser(@NonNull TargetUser user) { + onStopUser(user.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onCleanupUser(UserInfo)} instead (which by + * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by * default calls this method). + * + * @hide */ @Deprecated public void onCleanupUser(@UserIdInt int userId) {} /** + * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by + * default calls this method). + * + * @hide + */ + @Deprecated + public void onCleanupUser(@NonNull UserInfo user) { + onCleanupUser(user.id); + } + + /** * Called when an existing user is stopping, for system services to finalize any per-user * state they maintain for running users. This is called after all application process * teardown of the user is complete. * - * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this - * user. + * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * this user. * * <p>NOTE: When this callback is called, the CE storage for the target user may not be - * accessible already. Use {@link #onStopUser(UserInfo)} instead if you need to access the CE + * accessible already. Use {@link #onStopUser(TargetUser)} instead if you need to access the CE * storage. * - * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object - * referenced by {@link UserManagerService} and hence should not be modified. + * @param user target user */ - public void onCleanupUser(@NonNull UserInfo userInfo) { - onCleanupUser(userInfo.id); + public void onCleanupUser(@NonNull TargetUser user) { + onCleanupUser(user.getUserInfo()); } /** @@ -318,7 +450,7 @@ public abstract class SystemService { * @param name the name of the new service * @param service the service object */ - protected final void publishBinderService(String name, IBinder service) { + protected final void publishBinderService(@NonNull String name, @NonNull IBinder service) { publishBinderService(name, service, false); } @@ -330,7 +462,7 @@ public abstract class SystemService { * @param allowIsolated set to true to allow isolated sandboxed processes * to access this service */ - protected final void publishBinderService(String name, IBinder service, + protected final void publishBinderService(@NonNull String name, @NonNull IBinder service, boolean allowIsolated) { publishBinderService(name, service, allowIsolated, DUMP_FLAG_PRIORITY_DEFAULT); } @@ -343,6 +475,8 @@ public abstract class SystemService { * @param allowIsolated set to true to allow isolated sandboxed processes * to access this service * @param dumpPriority supported dump priority levels as a bitmask + * + * @hide */ protected final void publishBinderService(String name, IBinder service, boolean allowIsolated, int dumpPriority) { @@ -351,6 +485,8 @@ public abstract class SystemService { /** * Get a binder service by its name. + * + * @hide */ protected final IBinder getBinderService(String name) { return ServiceManager.getService(name); @@ -358,6 +494,8 @@ public abstract class SystemService { /** * Publish the service so it is only accessible to the system process. + * + * @hide */ protected final <T> void publishLocalService(Class<T> type, T service) { LocalServices.addService(type, service); @@ -365,6 +503,8 @@ public abstract class SystemService { /** * Get a local service by interface. + * + * @hide */ protected final <T> T getLocalService(Class<T> type) { return LocalServices.getService(type); diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index c7157981ada2..c0f43a81eca4 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -27,6 +27,7 @@ import android.os.UserHandle; import android.os.UserManagerInternal; import android.util.Slog; +import com.android.server.SystemService.TargetUser; import com.android.server.utils.TimingsTraceAndSlog; import java.io.File; @@ -264,26 +265,26 @@ public class SystemServiceManager { @UserIdInt int curUserId, @UserIdInt int prevUserId) { t.traceBegin("ssm." + onWhat + "User-" + curUserId); Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId); - final UserInfo curUserInfo = getUserInfo(curUserId); - final UserInfo prevUserInfo = prevUserId == UserHandle.USER_NULL ? null - : getUserInfo(prevUserId); + final TargetUser curUser = new TargetUser(getUserInfo(curUserId)); + final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null + : new TargetUser(getUserInfo(prevUserId)); final int serviceLen = mServices.size(); for (int i = 0; i < serviceLen; i++) { final SystemService service = mServices.get(i); final String serviceName = service.getClass().getName(); - boolean supported = service.isSupported(curUserInfo); + boolean supported = service.isSupportedUser(curUser); // Must check if either curUser or prevUser is supported (for example, if switching from // unsupported to supported, we still need to notify the services) - if (!supported && prevUserInfo != null) { - supported = service.isSupported(prevUserInfo); + if (!supported && prevUser != null) { + supported = service.isSupportedUser(prevUser); } if (!supported) { if (DEBUG) { Slog.d(TAG, "Skipping " + onWhat + "User-" + curUserId + " on service " + serviceName + " because it's not supported (curUser: " - + curUserInfo + ", prevUser:" + prevUserInfo + ")"); + + curUser + ", prevUser:" + prevUser + ")"); } else { Slog.i(TAG, "Skipping " + onWhat + "User-" + curUserId + " on " + serviceName); @@ -295,25 +296,25 @@ public class SystemServiceManager { try { switch (onWhat) { case SWITCH: - service.onSwitchUser(prevUserInfo, curUserInfo); + service.onSwitchUser(prevUser, curUser); break; case START: - service.onStartUser(curUserInfo); + service.onStartUser(curUser); break; case UNLOCK: - service.onUnlockUser(curUserInfo); + service.onUnlockUser(curUser); break; case STOP: - service.onStopUser(curUserInfo); + service.onStopUser(curUser); break; case CLEANUP: - service.onCleanupUser(curUserInfo); + service.onCleanupUser(curUser); break; default: throw new IllegalArgumentException(onWhat + " what?"); } } catch (Exception ex) { - Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUserInfo + Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser + " to service " + serviceName, ex); } warnIfTooLong(SystemClock.elapsedRealtime() - time, service, diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index b9f2e76c6ecb..e11008c246dd 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1562,8 +1562,7 @@ public final class ProcessList { throw e.rethrowAsRuntimeException(); } int numGids = 3; - if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER - || mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { + if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { numGids++; } /* diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 2b207827a5ef..8ae18ff68b66 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1645,10 +1645,9 @@ class UserController implements Handler.Callback { final boolean allow; final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId); if (mInjector.isCallerRecents(callingUid) - && callingUserId == getCurrentUserId() - && isSameProfileGroup) { - // If the caller is Recents and it is running in the current user, we then allow it - // to access its profiles. + && isSameProfileGroup(callingUserId, targetUserId)) { + // If the caller is Recents and the caller has ownership of the profile group, + // we then allow it to access its profiles. allow = true; } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid, callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index a7593c7a43ec..e3b761e0773d 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1759,8 +1759,9 @@ public class AppOpsService extends IAppOpsService.Stub { ? opNames.toArray(new String[opNames.size()]) : null; // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOps(uid, packageName, featureId, opNamesArray, filter, - beginTimeMillis, endTimeMillis, flags, callback); + mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps, + mHistoricalRegistry, uid, packageName, featureId, opNamesArray, filter, + beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse()); } @Override @@ -1778,8 +1779,9 @@ public class AppOpsService extends IAppOpsService.Stub { ? opNames.toArray(new String[opNames.size()]) : null; // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, featureId, opNamesArray, - filter, beginTimeMillis, endTimeMillis, flags, callback); + mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw, + mHistoricalRegistry, uid, packageName, featureId, opNamesArray, + filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse()); } @Override diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index aea6d8d24312..f636d67c3d1d 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -116,7 +116,8 @@ public class Nat464Xlat extends BaseNetworkObserver { && !lp.hasIpv4Address(); // If the network tells us it doesn't use clat, respect that. - final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat; + final boolean skip464xlat = (nai.netAgentConfig() != null) + && nai.netAgentConfig().skip464xlat; return supported && connected && isIpv6OnlyNetwork && !skip464xlat; } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 5e085ca293a4..c1ab55106ab1 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -23,9 +23,9 @@ import android.net.INetd; import android.net.INetworkMonitor; import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; -import android.net.NetworkMisc; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; import android.net.NetworkScore; @@ -127,7 +127,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // This should only be modified by ConnectivityService, via setNetworkCapabilities(). // TODO: make this private with a getter. public NetworkCapabilities networkCapabilities; - public final NetworkMisc networkMisc; + public final NetworkAgentConfig networkAgentConfig; // Indicates if netd has been told to create this Network. From this point on the appropriate // routing rules are setup and routes are added so packets can begin flowing over the Network. // This is a sticky bit; once set it is never cleared. @@ -261,7 +261,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, @NonNull NetworkScore ns, Context context, - Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd, + Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) { this.messenger = messenger; asyncChannel = ac; @@ -274,7 +274,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mConnService = connService; mContext = context; mHandler = handler; - networkMisc = misc; + networkAgentConfig = config; this.factorySerialNumber = factorySerialNumber; } @@ -309,8 +309,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return mConnService; } - public NetworkMisc netMisc() { - return networkMisc; + public NetworkAgentConfig netAgentConfig() { + return networkAgentConfig; } public Handler handler() { @@ -487,7 +487,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // selected and we're trying to see what its score could be. This ensures that we don't tear // down an explicitly selected network before the user gets a chance to prefer it when // a higher-scoring network (e.g., Ethernet) is available. - if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) { + if (networkAgentConfig.explicitlySelected + && (networkAgentConfig.acceptUnvalidated || pretendValidated)) { return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; } @@ -533,7 +534,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { synchronized (this) { // Network objects are outwardly immutable so there is no point in duplicating. // Duplicating also precludes sharing socket factories and connection pools. - final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null; + final String subscriberId = (networkAgentConfig != null) + ? networkAgentConfig.subscriberId : null; return new NetworkState(new NetworkInfo(networkInfo), new LinkProperties(linkProperties), new NetworkCapabilities(networkCapabilities), network, subscriberId, null); @@ -641,13 +643,13 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " + "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " + "created{" + created + "} lingering{" + isLingering() + "} " - + "explicitlySelected{" + networkMisc.explicitlySelected + "} " - + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " + + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} " + + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} " + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " + "captivePortalValidationPending{" + captivePortalValidationPending + "} " + "partialConnectivity{" + partialConnectivity + "} " - + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} " + + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} " + "clat{" + clatd + "} " + "}"; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 6b70e5f00330..476f3f823def 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -53,11 +53,11 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.net.Network; import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; -import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; -import android.net.NetworkMisc; +import android.net.NetworkProvider; import android.net.RouteInfo; import android.net.UidRange; import android.net.VpnService; @@ -914,7 +914,7 @@ public class Vpn { * has certain changes, in which case this method would just return {@code false}. */ private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) { - // NetworkMisc cannot be updated without registering a new NetworkAgent. + // NetworkAgentConfig cannot be updated without registering a new NetworkAgent. if (oldConfig.allowBypass != mConfig.allowBypass) { Log.i(TAG, "Handover not possible due to changes to allowBypass"); return false; @@ -947,8 +947,8 @@ public class Vpn { mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null); - NetworkMisc networkMisc = new NetworkMisc(); - networkMisc.allowBypass = mConfig.allowBypass && !mLockdown; + NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); + networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown; mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid()); mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle, @@ -957,8 +957,8 @@ public class Vpn { try { mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */, mNetworkInfo, mNetworkCapabilities, lp, - ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc, - NetworkFactory.SerialNumber.VPN) { + ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, + NetworkProvider.ID_VPN) { @Override public void unwanted() { // We are user controlled, not driven by NetworkRequest. diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index c58000fa6cee..8206fef90ab7 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -41,7 +41,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -713,8 +712,8 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } /** - * Gets a list of all supported users (i.e., those that pass the {@link #isSupported(UserInfo)} - * check). + * Gets a list of all supported users (i.e., those that pass the + * {@link #isSupportedUser(TargetUser)}check). */ @NonNull protected List<UserInfo> getSupportedUsers() { @@ -723,7 +722,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem final List<UserInfo> supportedUsers = new ArrayList<>(size); for (int i = 0; i < size; i++) { final UserInfo userInfo = allUsers[i]; - if (isSupported(userInfo)) { + if (isSupportedUser(new TargetUser(userInfo))) { supportedUsers.add(userInfo); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index c40e8af73268..2c3c70fc3da2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -72,6 +72,18 @@ public abstract class InputMethodManagerInternal { AutofillId autofillId, IInlineSuggestionsRequestCallback cb); /** + * Force switch to the enabled input method by {@code imeId} for current user. If the input + * method with {@code imeId} is not enabled or not installed, do nothing. + * + * @param imeId The input method ID to be switched to. + * @param userId The user ID to be queried. + * @return {@code true} if the current input method was successfully switched to the input + * method by {@code imeId}; {@code false} the input method with {@code imeId} is not available + * to be switched. + */ + public abstract boolean switchToInputMethod(String imeId, @UserIdInt int userId); + + /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = @@ -98,6 +110,11 @@ public abstract class InputMethodManagerInternal { public void onCreateInlineSuggestionsRequest(ComponentName componentName, AutofillId autofillId, IInlineSuggestionsRequestCallback cb) { } + + @Override + public boolean switchToInputMethod(String imeId, int userId) { + return false; + } }; /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 8b6b61499e9e..d4b13faf3195 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4478,6 +4478,38 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private boolean switchToInputMethod(String imeId, @UserIdInt int userId) { + synchronized (mMethodMap) { + if (userId == mSettings.getCurrentUserId()) { + if (!mMethodMap.containsKey(imeId) + || !mSettings.getEnabledInputMethodListLocked() + .contains(mMethodMap.get(imeId))) { + return false; // IME is not is found or not enabled. + } + setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); + return true; + } + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, + methodMap, methodList); + final InputMethodSettings settings = new InputMethodSettings( + mContext.getResources(), mContext.getContentResolver(), methodMap, + userId, false); + if (!methodMap.containsKey(imeId) + || !settings.getEnabledInputMethodListLocked() + .contains(methodMap.get(imeId))) { + return false; // IME is not is found or not enabled. + } + settings.putSelectedInputMethod(imeId); + settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + return true; + } + } + private static final class LocalServiceImpl extends InputMethodManagerInternal { @NonNull private final InputMethodManagerService mService; @@ -4514,6 +4546,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub AutofillId autofillId, IInlineSuggestionsRequestCallback cb) { mService.onCreateInlineSuggestionsRequest(componentName, autofillId, cb); } + + @Override + public boolean switchToInputMethod(String imeId, int userId) { + return mService.switchToInputMethod(imeId, userId); + } } @BinderThread @@ -5065,31 +5102,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!userHasDebugPriv(userId, shellCommand)) { continue; } - boolean failedToSelectUnknownIme = false; - if (userId == mSettings.getCurrentUserId()) { - if (mMethodMap.containsKey(imeId)) { - setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); - } else { - failedToSelectUnknownIme = true; - } - } else { - final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); - final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); - final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = - new ArrayMap<>(); - AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); - queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, - methodMap, methodList); - final InputMethodSettings settings = new InputMethodSettings( - mContext.getResources(), mContext.getContentResolver(), methodMap, - userId, false); - if (methodMap.containsKey(imeId)) { - settings.putSelectedInputMethod(imeId); - settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); - } else { - failedToSelectUnknownIme = true; - } - } + boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId); if (failedToSelectUnknownIme) { error.print("Unknown input method "); error.print(imeId); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 1f9379c259cf..20d7955e0c77 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -200,6 +200,12 @@ public final class MultiClientInputMethodManagerService { Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e); } } + + @Override + public boolean switchToInputMethod(String imeId, @UserIdInt int userId) { + reportNotSupported(); + return false; + } }); } diff --git a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java index e555e3e5746e..4bf8fe8d93b6 100644 --- a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java +++ b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java @@ -27,30 +27,37 @@ import java.io.InputStream; */ public class BitTrackedInputStream extends BitInputStream { - private static int sReadBitsCount; + private int mReadBitsCount; /** Constructor with byte array. */ public BitTrackedInputStream(byte[] inputStream) { super(inputStream); - sReadBitsCount = 0; + mReadBitsCount = 0; } /** Constructor with input stream. */ public BitTrackedInputStream(InputStream inputStream) { super(inputStream); - sReadBitsCount = 0; + mReadBitsCount = 0; } /** Obtains an integer value of the next {@code numOfBits}. */ @Override public int getNext(int numOfBits) throws IOException { - sReadBitsCount += numOfBits; + mReadBitsCount += numOfBits; return super.getNext(numOfBits); } /** Returns the current cursor position showing the number of bits that are read. */ public int getReadBitsCount() { - return sReadBitsCount; + return mReadBitsCount; + } + + /** + * Returns true if we can read more rules by checking whether the end index is not reached yet. + */ + public boolean canReadMoreRules(int endIndexBytes) { + return mReadBitsCount < endIndexBytes * 8; } /** @@ -59,11 +66,11 @@ public class BitTrackedInputStream extends BitInputStream { * Note that the integer parameter specifies the location in bytes -- not bits. */ public void setCursorToByteLocation(int byteLocation) throws IOException { - int bitCountToRead = byteLocation * 8 - sReadBitsCount; + int bitCountToRead = byteLocation * 8 - mReadBitsCount; if (bitCountToRead < 0) { throw new IllegalStateException("The byte position is already read."); } super.getNext(bitCountToRead); - sReadBitsCount = byteLocation * 8; + mReadBitsCount = byteLocation * 8; } } diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index e744326c49db..2f285631b982 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -105,8 +105,7 @@ public class RuleBinaryParser implements RuleParser { bitTrackedInputStream.setCursorToByteLocation(range.getStartIndex()); // Read the rules until we reach the end index. - while (bitTrackedInputStream.hasNext() - && bitTrackedInputStream.getReadBitsCount() < range.getEndIndex()) { + while (bitTrackedInputStream.canReadMoreRules(range.getEndIndex())) { if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) { parsedRules.add(parseRule(bitTrackedInputStream)); } diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java index 8c8450e5fdc4..453fa5d84053 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java +++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java @@ -23,28 +23,28 @@ import android.annotation.Nullable; * RuleIndexingController}. */ public class RuleIndexRange { - private static int sStartIndex; - private static int sEndIndex; + private int mStartIndex; + private int mEndIndex; /** Constructor with start and end indexes. */ public RuleIndexRange(int startIndex, int endIndex) { - this.sStartIndex = startIndex; - this.sEndIndex = endIndex; + this.mStartIndex = startIndex; + this.mEndIndex = endIndex; } /** Returns the startIndex. */ public int getStartIndex() { - return sStartIndex; + return mStartIndex; } /** Returns the end index. */ public int getEndIndex() { - return sEndIndex; + return mEndIndex; } @Override public boolean equals(@Nullable Object object) { - return sStartIndex == ((RuleIndexRange) object).getStartIndex() - && sEndIndex == ((RuleIndexRange) object).getEndIndex(); + return mStartIndex == ((RuleIndexRange) object).getStartIndex() + && mEndIndex == ((RuleIndexRange) object).getEndIndex(); } } diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java index c9713220d6e8..03392abf478f 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java +++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java @@ -56,7 +56,7 @@ public class RuleIndexingController { * read and evaluated. */ public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) { - ArrayList<RuleIndexRange> indexRanges = new ArrayList(); + List<RuleIndexRange> indexRanges = new ArrayList<>(); // Add the range for package name indexes rules. indexRanges.add( @@ -102,7 +102,7 @@ public class RuleIndexingController { .collect(Collectors.toCollection(TreeSet::new)); String minIndex = keyTreeSet.floor(searchedKey); - String maxIndex = keyTreeSet.ceiling(searchedKey); + String maxIndex = keyTreeSet.higher(searchedKey); return new RuleIndexRange( indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex), diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index f964d4cf2724..8f53be7d87af 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -48,10 +48,11 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.TreeMap; +import java.util.stream.Collectors; /** A helper class to serialize rules from the {@link Rule} model to Binary representation. */ public class RuleBinarySerializer implements RuleSerializer { @@ -79,31 +80,37 @@ public class RuleBinarySerializer implements RuleSerializer { throws RuleSerializeException { try { // Determine the indexing groups and the order of the rules within each indexed group. - Map<Integer, TreeMap<String, List<Rule>>> indexedRules = + Map<Integer, Map<String, List<Rule>>> indexedRules = RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules); + // Serialize the rules. ByteTrackedOutputStream ruleFileByteTrackedOutputStream = new ByteTrackedOutputStream(rulesFileOutputStream); - serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream); - - Map<String, Integer> packageNameIndexes = - serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED), + LinkedHashMap<String, Integer> packageNameIndexes = + serializeRuleList( + indexedRules.get(PACKAGE_NAME_INDEXED), ruleFileByteTrackedOutputStream); - indexingFileOutputStream.write( - serializeIndexes(packageNameIndexes, /* isIndexed= */true)); - - Map<String, Integer> appCertificateIndexes = - serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED), + LinkedHashMap<String, Integer> appCertificateIndexes = + serializeRuleList( + indexedRules.get(APP_CERTIFICATE_INDEXED), ruleFileByteTrackedOutputStream); - indexingFileOutputStream.write( - serializeIndexes(appCertificateIndexes, /* isIndexed= */true)); - - Map<String, Integer> unindexedRulesIndexes = - serializeRuleList(indexedRules.get(NOT_INDEXED), + LinkedHashMap<String, Integer> unindexedRulesIndexes = + serializeRuleList( + indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream); - indexingFileOutputStream.write( - serializeIndexes(unindexedRulesIndexes, /* isIndexed= */false)); + + // Serialize their indexes. + BitOutputStream indexingBitOutputStream = new BitOutputStream(); + serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true); + serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ + true); + serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ + false); + // TODO(b/147609625): This dummy bit is set for fixing the padding issue. Remove when + // the issue is fixed and correct the tests that does this padding too. + indexingBitOutputStream.setNext(); + indexingFileOutputStream.write(indexingBitOutputStream.toByteArray()); } catch (Exception e) { throw new RuleSerializeException(e.getMessage(), e); } @@ -119,24 +126,25 @@ public class RuleBinarySerializer implements RuleSerializer { outputStream.write(bitOutputStream.toByteArray()); } - private Map<String, Integer> serializeRuleList(TreeMap<String, List<Rule>> rulesMap, - ByteTrackedOutputStream outputStream) + private LinkedHashMap<String, Integer> serializeRuleList( + Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream) throws IOException { Preconditions.checkArgument(rulesMap != null, "serializeRuleList should never be called with null rule list."); BitOutputStream bitOutputStream = new BitOutputStream(); - Map<String, Integer> indexMapping = new TreeMap(); - int indexTracker = 0; - + LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap(); indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount()); - for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) { + + List<String> sortedKeys = rulesMap.keySet().stream().sorted().collect(Collectors.toList()); + int indexTracker = 0; + for (String key : sortedKeys) { if (indexTracker >= INDEXING_BLOCK_SIZE) { - indexMapping.put(entry.getKey(), outputStream.getWrittenBytesCount()); + indexMapping.put(key, outputStream.getWrittenBytesCount()); indexTracker = 0; } - for (Rule rule : entry.getValue()) { + for (Rule rule : rulesMap.get(key)) { bitOutputStream.clear(); serializeRule(rule, bitOutputStream); outputStream.write(bitOutputStream.toByteArray()); @@ -220,12 +228,14 @@ public class RuleBinarySerializer implements RuleSerializer { } } - private byte[] serializeIndexes(Map<String, Integer> indexes, boolean isIndexed) { - BitOutputStream bitOutputStream = new BitOutputStream(); + private void serializeIndexGroup( + LinkedHashMap<String, Integer> indexes, + BitOutputStream bitOutputStream, + boolean isIndexed) { // Output the starting location of this indexing group. - serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */false, - bitOutputStream); + serializeStringValue( + START_INDEXING_KEY, /* isHashedValue= */false, bitOutputStream); serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream); // If the group is indexed, output the locations of the indexes. @@ -243,8 +253,6 @@ public class RuleBinarySerializer implements RuleSerializer { // Output the end location of this indexing group. serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream); serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream); - - return bitOutputStream.toByteArray(); } private void serializeStringValue( diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java index dd871e2bbe6c..2cbd4ede5214 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java @@ -28,6 +28,8 @@ class RuleIndexingDetails { static final int PACKAGE_NAME_INDEXED = 1; static final int APP_CERTIFICATE_INDEXED = 2; + static final String DEFAULT_RULE_KEY = "N/A"; + /** Represents which indexed file the rule should be located. */ @IntDef( value = { @@ -45,7 +47,7 @@ class RuleIndexingDetails { /** Constructor without a ruleKey for {@code NOT_INDEXED}. */ RuleIndexingDetails(@IndexType int indexType) { this.mIndexType = indexType; - this.mRuleKey = null; + this.mRuleKey = DEFAULT_RULE_KEY; } /** Constructor with a ruleKey for indexed rules. */ diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java index cbc365e2c250..7d9a90188983 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java @@ -30,30 +30,27 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.TreeMap; /** A helper class for identifying the indexing type and key of a given rule. */ class RuleIndexingDetailsIdentifier { - private static final String DEFAULT_RULE_KEY = "N/A"; - /** * Splits a given rule list into three indexing categories. Each rule category is returned as a * TreeMap that is sorted by their indexing keys -- where keys correspond to package name for * PACKAGE_NAME_INDEXED rules, app certificate for APP_CERTIFICATE_INDEXED rules and N/A for * NOT_INDEXED rules. */ - public static Map<Integer, TreeMap<String, List<Rule>>> splitRulesIntoIndexBuckets( + public static Map<Integer, Map<String, List<Rule>>> splitRulesIntoIndexBuckets( List<Rule> rules) { if (rules == null) { throw new IllegalArgumentException( "Index buckets cannot be created for null rule list."); } - Map<Integer, TreeMap<String, List<Rule>>> typeOrganizedRuleMap = new HashMap(); - typeOrganizedRuleMap.put(NOT_INDEXED, new TreeMap()); - typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new TreeMap()); - typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new TreeMap()); + Map<Integer, Map<String, List<Rule>>> typeOrganizedRuleMap = new HashMap(); + typeOrganizedRuleMap.put(NOT_INDEXED, new HashMap()); + typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new HashMap<>()); + typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new HashMap<>()); // Split the rules into the appropriate indexed pattern. The Tree Maps help us to keep the // entries sorted by their index key. @@ -66,21 +63,14 @@ class RuleIndexingDetailsIdentifier { String.format("Malformed rule identified. [%s]", rule.toString())); } - String ruleKey = - indexingDetails.getIndexType() != NOT_INDEXED - ? indexingDetails.getRuleKey() - : DEFAULT_RULE_KEY; + int ruleIndexType = indexingDetails.getIndexType(); + String ruleKey = indexingDetails.getRuleKey(); - if (!typeOrganizedRuleMap.get(indexingDetails.getIndexType()).containsKey(ruleKey)) { - typeOrganizedRuleMap - .get(indexingDetails.getIndexType()) - .put(ruleKey, new ArrayList()); + if (!typeOrganizedRuleMap.get(ruleIndexType).containsKey(ruleKey)) { + typeOrganizedRuleMap.get(ruleIndexType).put(ruleKey, new ArrayList()); } - typeOrganizedRuleMap - .get(indexingDetails.getIndexType()) - .get(ruleKey) - .add(rule); + typeOrganizedRuleMap.get(ruleIndexType).get(ruleKey).add(rule); } return typeOrganizedRuleMap; diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java index 4194432375b8..8f164e645434 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java @@ -35,7 +35,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.TreeMap; +import java.util.stream.Collectors; /** A helper class to serialize rules from the {@link Rule} model to Xml representation. */ public class RuleXmlSerializer implements RuleSerializer { @@ -90,7 +90,7 @@ public class RuleXmlSerializer implements RuleSerializer { throws RuleSerializeException { try { // Determine the indexing groups and the order of the rules within each indexed group. - Map<Integer, TreeMap<String, List<Rule>>> indexedRules = + Map<Integer, Map<String, List<Rule>>> indexedRules = RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules); // Write the XML formatted rules in order. @@ -107,11 +107,12 @@ public class RuleXmlSerializer implements RuleSerializer { } } - private void serializeRuleList(TreeMap<String, List<Rule>> rulesMap, - XmlSerializer xmlSerializer) + private void serializeRuleList(Map<String, List<Rule>> rulesMap, XmlSerializer xmlSerializer) throws IOException { - for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) { - for (Rule rule : entry.getValue()) { + List<String> sortedKeyList = + rulesMap.keySet().stream().sorted().collect(Collectors.toList()); + for (String key : sortedKeyList) { + for (Rule rule : rulesMap.get(key)) { serializeRule(rule, xmlSerializer); } } diff --git a/services/core/java/com/android/server/location/NtpTimeHelper.java b/services/core/java/com/android/server/location/NtpTimeHelper.java index 67841aca1605..d2296ea27913 100644 --- a/services/core/java/com/android/server/location/NtpTimeHelper.java +++ b/services/core/java/com/android/server/location/NtpTimeHelper.java @@ -130,7 +130,8 @@ class NtpTimeHelper { // force refresh NTP cache when outdated boolean refreshSuccess = true; - if (mNtpTime.getCacheAge() >= NTP_INTERVAL) { + NtpTrustedTime.TimeResult ntpResult = mNtpTime.getCachedTimeResult(); + if (ntpResult == null || ntpResult.getAgeMillis() >= NTP_INTERVAL) { // Blocking network operation. refreshSuccess = mNtpTime.forceRefresh(); } @@ -140,17 +141,17 @@ class NtpTimeHelper { // only update when NTP time is fresh // If refreshSuccess is false, cacheAge does not drop down. - if (mNtpTime.getCacheAge() < NTP_INTERVAL) { - long time = mNtpTime.getCachedNtpTime(); - long timeReference = mNtpTime.getCachedNtpTimeReference(); - long certainty = mNtpTime.getCacheCertainty(); + ntpResult = mNtpTime.getCachedTimeResult(); + if (ntpResult != null && ntpResult.getAgeMillis() < NTP_INTERVAL) { + long time = ntpResult.getTimeMillis(); + long timeReference = ntpResult.getElapsedRealtimeMillis(); + long certainty = ntpResult.getCertaintyMillis(); if (DEBUG) { long now = System.currentTimeMillis(); Log.d(TAG, "NTP server returned: " - + time + " (" + new Date(time) - + ") reference: " + timeReference - + " certainty: " + certainty + + time + " (" + new Date(time) + ")" + + " ntpResult: " + ntpResult + " system time offset: " + (time - now)); } diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java new file mode 100644 index 000000000000..5191833f367e --- /dev/null +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -0,0 +1,321 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.annotation.NonNull; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.MediaRoute2Info; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseBooleanArray; + +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +class BluetoothRouteProvider { + private static final String TAG = "BTRouteProvider"; + private static BluetoothRouteProvider sInstance; + + @SuppressWarnings("WeakerAccess") /* synthetic access */ + final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>(); + @SuppressWarnings("WeakerAccess") /* synthetic access */ + BluetoothA2dp mA2dpProfile; + @SuppressWarnings("WeakerAccess") /* synthetic access */ + BluetoothHearingAid mHearingAidProfile; + + private final Context mContext; + private final BluetoothAdapter mBluetoothAdapter; + private final BluetoothRoutesUpdatedListener mListener; + private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>(); + private final IntentFilter mIntentFilter = new IntentFilter(); + private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver(); + private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener(); + + private BluetoothDevice mActiveDevice = null; + + static synchronized BluetoothRouteProvider getInstance(@NonNull Context context, + @NonNull BluetoothRoutesUpdatedListener listener) { + Objects.requireNonNull(context); + Objects.requireNonNull(listener); + + if (sInstance == null) { + BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + if (btAdapter == null) { + return null; + } + sInstance = new BluetoothRouteProvider(context, btAdapter, listener); + } + return sInstance; + } + + private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter, + BluetoothRoutesUpdatedListener listener) { + mContext = context; + mBluetoothAdapter = btAdapter; + mListener = listener; + buildBluetoothRoutes(); + + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); + + // Bluetooth on/off broadcasts + addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver()); + + // Pairing broadcasts + addEventReceiver(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedReceiver()); + + DeviceStateChangedRecevier deviceStateChangedReceiver = new DeviceStateChangedRecevier(); + addEventReceiver(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver); + addEventReceiver(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); + addEventReceiver(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED, + deviceStateChangedReceiver); + addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED, + deviceStateChangedReceiver); + + mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null); + } + + private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) { + mEventReceiverMap.put(action, eventReceiver); + mIntentFilter.addAction(action); + } + + private void buildBluetoothRoutes() { + mBluetoothRoutes.clear(); + for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) { + if (device.isConnected()) { + BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); + mBluetoothRoutes.put(device.getAddress(), newBtRoute); + } + } + } + + @NonNull List<MediaRoute2Info> getBluetoothRoutes() { + ArrayList<MediaRoute2Info> routes = new ArrayList<>(); + for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { + routes.add(btRoute.route); + } + return routes; + } + + private void notifyBluetoothRoutesUpdated() { + if (mListener != null) { + mListener.onBluetoothRoutesUpdated(getBluetoothRoutes()); + } + } + + private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) { + BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo(); + newBtRoute.btDevice = device; + newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName()) + .addFeature(SystemMediaRoute2Provider.TYPE_LIVE_AUDIO) + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED) + .setDescription(mContext.getResources().getText( + R.string.bluetooth_a2dp_audio_route_name).toString()) + .build(); + newBtRoute.connectedProfiles = new SparseBooleanArray(); + return newBtRoute; + } + + private void setRouteConnectionStateForDevice(BluetoothDevice device, + @MediaRoute2Info.ConnectionState int state) { + if (device == null) { + Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null"); + return; + } + BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); + if (btRoute == null) { + Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null"); + return; + } + if (btRoute.route.getConnectionState() != state) { + btRoute.route = new MediaRoute2Info.Builder(btRoute.route) + .setConnectionState(state).build(); + } + } + + interface BluetoothRoutesUpdatedListener { + void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes); + } + + private class BluetoothRouteInfo { + public BluetoothDevice btDevice; + public MediaRoute2Info route; + public SparseBooleanArray connectedProfiles; + } + + // These callbacks run on the main thread. + private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener { + public void onServiceConnected(int profile, BluetoothProfile proxy) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dpProfile = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEARING_AID: + mHearingAidProfile = (BluetoothHearingAid) proxy; + break; + default: + return; + } + for (BluetoothDevice device : proxy.getConnectedDevices()) { + BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); + if (btRoute == null) { + btRoute = createBluetoothRoute(device); + mBluetoothRoutes.put(device.getAddress(), btRoute); + } + btRoute.connectedProfiles.put(profile, true); + } + } + + public void onServiceDisconnected(int profile) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dpProfile = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAidProfile = null; + break; + default: + return; + } + } + } + private class BluetoothBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + + BluetoothEventReceiver receiver = mEventReceiverMap.get(action); + if (receiver != null) { + receiver.onReceive(context, intent, device); + } + } + } + + private interface BluetoothEventReceiver { + void onReceive(Context context, Intent intent, BluetoothDevice device); + } + + private class AdapterStateChangedReceiver implements BluetoothEventReceiver { + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + if (state == BluetoothAdapter.STATE_OFF + || state == BluetoothAdapter.STATE_TURNING_OFF) { + mBluetoothRoutes.clear(); + notifyBluetoothRoutesUpdated(); + } else if (state == BluetoothAdapter.STATE_ON) { + buildBluetoothRoutes(); + if (!mBluetoothRoutes.isEmpty()) { + notifyBluetoothRoutesUpdated(); + } + } + } + } + + private class BondStateChangedReceiver implements BluetoothEventReceiver { + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, + BluetoothDevice.ERROR); + BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); + if (bondState == BluetoothDevice.BOND_BONDED && btRoute == null) { + btRoute = createBluetoothRoute(device); + if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) { + btRoute.connectedProfiles.put(BluetoothProfile.A2DP, true); + } + if (mHearingAidProfile != null + && mHearingAidProfile.getConnectedDevices().contains(device)) { + btRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true); + } + mBluetoothRoutes.put(device.getAddress(), btRoute); + notifyBluetoothRoutesUpdated(); + } else if (bondState == BluetoothDevice.BOND_NONE + && mBluetoothRoutes.remove(device.getAddress()) != null) { + notifyBluetoothRoutesUpdated(); + } + } + } + + private class DeviceStateChangedRecevier implements BluetoothEventReceiver { + @Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + switch (intent.getAction()) { + case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED: + String prevActiveDeviceAddress = + (mActiveDevice == null) ? null : mActiveDevice.getAddress(); + String curActiveDeviceAddress = + (device == null) ? null : device.getAddress(); + if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) { + if (mActiveDevice != null) { + setRouteConnectionStateForDevice(mActiveDevice, + MediaRoute2Info.CONNECTION_STATE_DISCONNECTED); + } + if (device != null) { + setRouteConnectionStateForDevice(device, + MediaRoute2Info.CONNECTION_STATE_CONNECTED); + } + notifyBluetoothRoutesUpdated(); + mActiveDevice = device; + } + break; + case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: + handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device); + break; + } + } + + private void handleConnectionStateChanged(int profile, Intent intent, + BluetoothDevice device) { + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); + BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); + if (state == BluetoothProfile.STATE_CONNECTED) { + if (btRoute == null) { + btRoute = createBluetoothRoute(device); + mBluetoothRoutes.put(device.getAddress(), btRoute); + btRoute.connectedProfiles.put(profile, true); + notifyBluetoothRoutesUpdated(); + } else { + btRoute.connectedProfiles.put(profile, true); + } + } else if (state == BluetoothProfile.STATE_DISCONNECTING + || state == BluetoothProfile.STATE_DISCONNECTED) { + btRoute.connectedProfiles.delete(profile); + if (btRoute.connectedProfiles.size() == 0) { + mBluetoothRoutes.remove(device.getAddress()); + if (mActiveDevice != null + && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) { + mActiveDevice = null; + } + notifyBluetoothRoutesUpdated(); + } + } + } + } +} diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 9c9a4121830f..0d315cd6863d 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Intent; import android.media.MediaRoute2ProviderInfo; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import java.util.ArrayList; import java.util.Collections; @@ -34,7 +34,7 @@ abstract class MediaRoute2Provider { Callback mCallback; private volatile MediaRoute2ProviderInfo mProviderInfo; - private volatile List<RouteSessionInfo> mSessionInfos = Collections.emptyList(); + private volatile List<RoutingSessionInfo> mSessionInfos = Collections.emptyList(); MediaRoute2Provider(@NonNull ComponentName componentName) { mComponentName = Objects.requireNonNull(componentName, "Component name must not be null."); @@ -68,12 +68,12 @@ abstract class MediaRoute2Provider { } @NonNull - public List<RouteSessionInfo> getSessionInfos() { + public List<RoutingSessionInfo> getSessionInfos() { return mSessionInfos; } - void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, - List<RouteSessionInfo> sessionInfos) { + void setProviderState(MediaRoute2ProviderInfo providerInfo, + List<RoutingSessionInfo> sessionInfos) { if (providerInfo == null) { mProviderInfo = null; } else { @@ -81,20 +81,28 @@ abstract class MediaRoute2Provider { .setUniqueId(mUniqueId) .build(); } - List<RouteSessionInfo> sessionInfoWithProviderId = new ArrayList<RouteSessionInfo>(); - for (RouteSessionInfo sessionInfo : sessionInfos) { + List<RoutingSessionInfo> sessionInfoWithProviderId = new ArrayList<RoutingSessionInfo>(); + for (RoutingSessionInfo sessionInfo : sessionInfos) { sessionInfoWithProviderId.add( - new RouteSessionInfo.Builder(sessionInfo) + new RoutingSessionInfo.Builder(sessionInfo) .setProviderId(mUniqueId) .build()); } mSessionInfos = sessionInfoWithProviderId; + } + void notifyProviderState() { if (mCallback != null) { mCallback.onProviderStateChanged(this); } } + void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, + List<RoutingSessionInfo> sessionInfos) { + setProviderState(providerInfo, sessionInfos); + notifyProviderState(); + } + public boolean hasComponentName(String packageName, String className) { return mComponentName.getPackageName().equals(packageName) && mComponentName.getClassName().equals(className); @@ -103,12 +111,12 @@ abstract class MediaRoute2Provider { public interface Callback { void onProviderStateChanged(@Nullable MediaRoute2Provider provider); void onSessionCreated(@NonNull MediaRoute2Provider provider, - @Nullable RouteSessionInfo sessionInfo, long requestId); + @Nullable RoutingSessionInfo sessionInfo, long requestId); // TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes. void onSessionInfoChanged(@NonNull MediaRoute2Provider provider, - @NonNull RouteSessionInfo sessionInfo); + @NonNull RoutingSessionInfo sessionInfo); // TODO: Call this when service actually notifies of session release. void onSessionReleased(@NonNull MediaRoute2Provider provider, - @NonNull RouteSessionInfo sessionInfo); + @NonNull RoutingSessionInfo sessionInfo); } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 635983575226..c0ad46f5fa06 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -26,7 +26,7 @@ import android.media.IMediaRoute2Provider; import android.media.IMediaRoute2ProviderClient; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; @@ -270,7 +270,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } private void onProviderStateUpdated(Connection connection, - MediaRoute2ProviderInfo providerInfo, List<RouteSessionInfo> sessionInfos) { + MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { if (mActiveConnection != connection) { return; } @@ -280,20 +280,20 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv setAndNotifyProviderState(providerInfo, sessionInfos); } - private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo, + private void onSessionCreated(Connection connection, @Nullable RoutingSessionInfo sessionInfo, long requestId) { if (mActiveConnection != connection) { return; } if (sessionInfo != null) { - sessionInfo = new RouteSessionInfo.Builder(sessionInfo) + sessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .setProviderId(getUniqueId()) .build(); } mCallback.onSessionCreated(this, sessionInfo, requestId); } - private void onSessionInfoChanged(Connection connection, RouteSessionInfo sessionInfo) { + private void onSessionInfoChanged(Connection connection, RoutingSessionInfo sessionInfo) { if (mActiveConnection != connection) { return; } @@ -303,7 +303,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv return; } - sessionInfo = new RouteSessionInfo.Builder(sessionInfo) + sessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .setProviderId(getUniqueId()) .build(); @@ -422,17 +422,17 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo, - List<RouteSessionInfo> sessionInfos) { + List<RoutingSessionInfo> sessionInfos) { mHandler.post(() -> onProviderStateUpdated(Connection.this, providerInfo, sessionInfos)); } - void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) { + void postSessionCreated(@Nullable RoutingSessionInfo sessionInfo, long requestId) { mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo, requestId)); } - void postSessionInfoChanged(RouteSessionInfo sessionInfo) { + void postSessionInfoChanged(RoutingSessionInfo sessionInfo) { mHandler.post(() -> onSessionInfoChanged(Connection.this, sessionInfo)); } } @@ -450,7 +450,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv @Override public void updateState(MediaRoute2ProviderInfo providerInfo, - List<RouteSessionInfo> sessionInfos) { + List<RoutingSessionInfo> sessionInfos) { Connection connection = mConnectionRef.get(); if (connection != null) { connection.postProviderStateUpdated(providerInfo, sessionInfos); @@ -458,7 +458,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) { + public void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, long requestId) { Connection connection = mConnectionRef.get(); if (connection != null) { connection.postSessionCreated(sessionInfo, requestId); @@ -466,7 +466,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void notifySessionInfoChanged(RouteSessionInfo sessionInfo) { + public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) { Connection connection = mConnectionRef.get(); if (connection != null) { connection.postSessionInfoChanged(sessionInfo); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 771edb26624d..c48c90db30d1 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -32,7 +32,7 @@ import android.media.IMediaRouter2Manager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.RouteDiscoveryPreference; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -370,7 +370,7 @@ class MediaRouter2ServiceImpl { } @NonNull - public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) { + public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -653,7 +653,7 @@ class MediaRouter2ServiceImpl { } } - private List<RouteSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) { + private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -661,7 +661,7 @@ class MediaRouter2ServiceImpl { return Collections.emptyList(); } - List<RouteSessionInfo> sessionInfos = new ArrayList<>(); + List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) { sessionInfos.addAll(provider.getSessionInfos()); } @@ -876,20 +876,21 @@ class MediaRouter2ServiceImpl { @Override public void onSessionCreated(@NonNull MediaRoute2Provider provider, - @Nullable RouteSessionInfo sessionInfo, long requestId) { + @Nullable RoutingSessionInfo sessionInfo, long requestId) { sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler, this, provider, sessionInfo, requestId)); } @Override public void onSessionInfoChanged(@NonNull MediaRoute2Provider provider, - @NonNull RouteSessionInfo sessionInfo) { + @NonNull RoutingSessionInfo sessionInfo) { sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler, this, provider, sessionInfo)); } @Override - public void onSessionReleased(MediaRoute2Provider provider, RouteSessionInfo sessionInfo) { + public void onSessionReleased(MediaRoute2Provider provider, + RoutingSessionInfo sessionInfo) { sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler, this, provider, sessionInfo)); } @@ -1131,7 +1132,7 @@ class MediaRouter2ServiceImpl { } private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider, - @Nullable RouteSessionInfo sessionInfo, long requestId) { + @Nullable RoutingSessionInfo sessionInfo, long requestId) { SessionCreationRequest matchingRequest = null; for (SessionCreationRequest request : mSessionCreationRequests) { @@ -1182,7 +1183,7 @@ class MediaRouter2ServiceImpl { } private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, - @NonNull RouteSessionInfo sessionInfo) { + @NonNull RoutingSessionInfo sessionInfo) { Client2Record client2Record = mSessionToClientMap.get( sessionInfo.getId()); @@ -1196,7 +1197,7 @@ class MediaRouter2ServiceImpl { } private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, - @NonNull RouteSessionInfo sessionInfo) { + @NonNull RoutingSessionInfo sessionInfo) { Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId()); if (client2Record == null) { @@ -1208,8 +1209,8 @@ class MediaRouter2ServiceImpl { // TODO: Tell managers for the session release } - private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo, - int requestId) { + private void notifySessionCreated(Client2Record clientRecord, + RoutingSessionInfo sessionInfo, int requestId) { try { clientRecord.mClient.notifySessionCreated(sessionInfo, requestId); } catch (RemoteException ex) { @@ -1228,7 +1229,7 @@ class MediaRouter2ServiceImpl { } private void notifySessionInfoChanged(Client2Record clientRecord, - RouteSessionInfo sessionInfo) { + RoutingSessionInfo sessionInfo) { try { clientRecord.mClient.notifySessionInfoChanged(sessionInfo); } catch (RemoteException ex) { @@ -1238,7 +1239,7 @@ class MediaRouter2ServiceImpl { } private void notifySessionReleased(Client2Record clientRecord, - RouteSessionInfo sessionInfo) { + RoutingSessionInfo sessionInfo) { try { clientRecord.mClient.notifySessionReleased(sessionInfo); } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 2dbaab4c893a..b7aa484a3fc7 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -40,7 +40,7 @@ import android.media.MediaRouterClientState; import android.media.RemoteDisplayState; import android.media.RemoteDisplayState.RemoteDisplayInfo; import android.media.RouteDiscoveryPreference; -import android.media.RouteSessionInfo; +import android.media.RoutingSessionInfo; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -552,7 +552,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) { + public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) { return mService2.getActiveSessions(manager); } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index be0272cca9e5..961d3d01a67d 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -30,12 +30,12 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; -import android.text.TextUtils; import android.util.Log; import com.android.internal.R; import java.util.Collections; +import java.util.List; /** * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. @@ -55,6 +55,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; + private final BluetoothRouteProvider mBtRouteProvider; private static ComponentName sComponentName = new ComponentName( SystemMediaRoute2Provider.class.getPackageName$(), @@ -62,7 +63,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { //TODO: Clean up these when audio manager support multiple bt devices MediaRoute2Info mDefaultRoute; - MediaRoute2Info mBluetoothA2dpRoute; + @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { @@ -87,6 +88,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); + mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { + mBluetoothRoutes = routes; + publishRoutes(); + }); initializeRoutes(); } @@ -157,7 +162,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { updateAudioRoutes(newAudioRoutes); } - publishRoutes(); + mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes(); + + MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); + builder.addRoute(mDefaultRoute); + for (MediaRoute2Info route : mBluetoothRoutes) { + builder.addRoute(route); + } + setProviderState(builder.build(), Collections.emptyList()); + mHandler.post(() -> notifyProviderState()); } void updateAudioRoutes(AudioRoutesInfo newRoutes) { @@ -185,21 +198,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { .addFeature(TYPE_LIVE_VIDEO) .build(); - if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) { - mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName; - if (mCurAudioRoutesInfo.bluetoothName != null) { - //TODO: mark as bluetooth once MediaRoute2Info has device type - mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID, - mCurAudioRoutesInfo.bluetoothName) - .setDescription(mContext.getResources().getText( - R.string.bluetooth_a2dp_audio_route_name).toString()) - .addFeature(TYPE_LIVE_AUDIO) - .build(); - } else { - mBluetoothA2dpRoute = null; - } - } - publishRoutes(); } @@ -207,15 +205,13 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { * The first route should be the currently selected system route. * For example, if there are two system routes (BT and device speaker), * BT will be the first route in the list. - * - * TODO: Support multiple BT devices */ void publishRoutes() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); - if (mBluetoothA2dpRoute != null) { - builder.addRoute(mBluetoothA2dpRoute); - } builder.addRoute(mDefaultRoute); + for (MediaRoute2Info route : mBluetoothRoutes) { + builder.addRoute(route); + } setAndNotifyProviderState(builder.build(), Collections.emptyList()); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 7f650ee7e138..b24a938ef7ea 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -18,8 +18,10 @@ package com.android.server.net; import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal; +import android.annotation.NonNull; import android.net.Network; import android.net.NetworkTemplate; +import android.net.netstats.provider.AbstractNetworkStatsProvider; import android.telephony.SubscriptionPlan; import java.util.Set; @@ -126,4 +128,12 @@ public abstract class NetworkPolicyManagerInternal { */ public abstract void setMeteredRestrictedPackagesAsync( Set<String> packageNames, int userId); + + /** + * Notifies that any of the {@link AbstractNetworkStatsProvider} has reached its quota + * which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}. + * + * @param tag the human readable identifier of the custom network stats provider. + */ + public abstract void onStatsProviderLimitReached(@NonNull String tag); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 4a45730ccca5..d8a4655815ad 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -75,6 +75,7 @@ import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.TrafficStats.MB_IN_BYTES; +import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED; @@ -391,6 +392,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17; private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18; private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19; + private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20; private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -4518,6 +4520,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.finishBroadcast(); return true; } + case MSG_STATS_PROVIDER_LIMIT_REACHED: { + mNetworkStats.forceUpdate(); + synchronized (mNetworkPoliciesSecondLock) { + updateNetworkEnabledNL(); + updateNotificationsNL(); + } + return true; + } case MSG_LIMIT_REACHED: { final String iface = (String) msg.obj; @@ -4573,14 +4583,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return true; } case MSG_UPDATE_INTERFACE_QUOTA: { - removeInterfaceQuota((String) msg.obj); + final String iface = (String) msg.obj; // int params need to be stitched back into a long - setInterfaceQuota((String) msg.obj, - ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL)); + final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL); + removeInterfaceQuota(iface); + setInterfaceQuota(iface, quota); + mNetworkStats.setStatsProviderLimit(iface, quota); return true; } case MSG_REMOVE_INTERFACE_QUOTA: { - removeInterfaceQuota((String) msg.obj); + final String iface = (String) msg.obj; + removeInterfaceQuota(iface); + mNetworkStats.setStatsProviderLimit(iface, QUOTA_UNLIMITED); return true; } case MSG_RESET_FIREWALL_RULES_BY_UID: { @@ -5235,6 +5249,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED, userId, 0, packageNames).sendToTarget(); } + + @Override + public void onStatsProviderLimitReached(@NonNull String tag) { + Log.v(TAG, "onStatsProviderLimitReached: " + tag); + mHandler.obtainMessage(MSG_STATS_PROVIDER_LIMIT_REACHED).sendToTarget(); + } } private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) { diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java index 4843ede7219d..6d72cb5ee345 100644 --- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java @@ -16,6 +16,7 @@ package com.android.server.net; +import android.annotation.NonNull; import android.net.NetworkStats; import android.net.NetworkTemplate; @@ -34,4 +35,10 @@ public abstract class NetworkStatsManagerInternal { /** Force update of statistics. */ public abstract void forceUpdate(); + + /** + * Set the quota limit to all registered custom network stats providers. + * Note that invocation of any interface will be sent to all providers. + */ + public abstract void setStatsProviderLimit(@NonNull String iface, long quota); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 7a6f29764f09..1dcff07e36a2 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -18,6 +18,7 @@ package com.android.server.net; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; +import static android.Manifest.permission.UPDATE_DEVICE_STATS; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.ACTION_USER_REMOVED; @@ -71,6 +72,7 @@ import static com.android.server.NetworkManagementSocketTagger.resetKernelUidSta import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.usage.NetworkStatsManager; @@ -97,6 +99,9 @@ import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; +import android.net.netstats.provider.INetworkStatsProvider; +import android.net.netstats.provider.INetworkStatsProviderCallback; +import android.net.netstats.provider.NetworkStatsProviderCallback; import android.os.BestClock; import android.os.Binder; import android.os.DropBoxManager; @@ -109,6 +114,7 @@ import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.PowerManager; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -176,7 +182,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * This avoids firing the global alert too often on devices with high transfer speeds and * high quota. */ - private static final int PERFORM_POLL_DELAY_MS = 1000; + private static final int DEFAULT_PERFORM_POLL_DELAY_MS = 1000; private static final String TAG_NETSTATS_ERROR = "netstats_error"; @@ -220,6 +226,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ public interface NetworkStatsSettings { public long getPollInterval(); + public long getPollDelay(); public boolean getSampleEnabled(); public boolean getAugmentEnabled(); @@ -248,6 +255,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } private final Object mStatsLock = new Object(); + private final Object mStatsProviderLock = new Object(); /** Set of currently active ifaces. */ @GuardedBy("mStatsLock") @@ -272,6 +280,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); + private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList = + new RemoteCallbackList<>(); + @GuardedBy("mStatsLock") private NetworkStatsRecorder mDevRecorder; @GuardedBy("mStatsLock") @@ -502,9 +513,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * Register for a global alert that is delivered through - * {@link INetworkManagementEventObserver} once a threshold amount of data - * has been transferred. + * Register for a global alert that is delivered through {@link INetworkManagementEventObserver} + * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has + * been transferred. */ private void registerGlobalAlert() { try { @@ -514,6 +525,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } catch (RemoteException e) { // ignored; service lives in system_server } + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes)); } @Override @@ -803,8 +815,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void incrementOperationCount(int uid, int tag, int operationCount) { if (Binder.getCallingUid() != uid) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.UPDATE_DEVICE_STATS, TAG); + mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG); } if (operationCount < 0) { @@ -1095,7 +1106,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** * Observer that watches for {@link INetworkManagementService} alerts. */ - private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() { + private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() { @Override public void limitReached(String limitName, String iface) { // only someone like NMS should be calling us @@ -1106,7 +1117,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // such a call pending; UID stats are handled during normal polling interval. if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) { mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT, - PERFORM_POLL_DELAY_MS); + mSettings.getPollDelay()); } } } @@ -1252,6 +1263,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { xtSnapshot.combineAllValues(tetherSnapshot); devSnapshot.combineAllValues(tetherSnapshot); + // Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data + // from stats providers that isn't already counted by dev and XT stats. + Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider"); + final NetworkStats providersnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE); + Trace.traceEnd(TRACE_TAG_NETWORK); + xtSnapshot.combineAllValues(providersnapshot); + devSnapshot.combineAllValues(providersnapshot); + // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic // can't be reattributed to responsible apps. Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev"); @@ -1355,6 +1374,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performSampleLocked(); } + // request asynchronous stats update from all providers for next poll. + // TODO: request with a valid token. + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */)); + // finally, dispatch updated event to any listeners final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED); updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -1476,6 +1499,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public void forceUpdate() { NetworkStatsService.this.forceUpdate(); } + + @Override + public void setStatsProviderLimit(@NonNull String iface, long quota) { + Slog.v(TAG, "setStatsProviderLimit(" + iface + "," + quota + ")"); + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota)); + } } @Override @@ -1690,6 +1719,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { uidSnapshot.combineAllValues(vtStats); } + // get a stale copy of uid stats snapshot provided by providers. + final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID); + providerStats.filter(UID_ALL, ifaces, TAG_ALL); + mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats, mUseBpfTrafficStats); + uidSnapshot.combineAllValues(providerStats); + uidSnapshot.combineAllValues(mUidOperations); return uidSnapshot; @@ -1726,6 +1761,152 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + /** + * Registers a custom provider of {@link android.net.NetworkStats} to combine the network + * statistics that cannot be seen by the kernel to system. To unregister, invoke the + * {@code unregister()} of the returned callback. + * + * @param tag a human readable identifier of the custom network stats provider. + * @param provider the binder interface of + * {@link android.net.netstats.provider.AbstractNetworkStatsProvider} that + * needs to be registered to the system. + * + * @return a binder interface of + * {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be + * used to report events to the system. + */ + public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider( + @NonNull String tag, @NonNull INetworkStatsProvider provider) { + mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG); + Objects.requireNonNull(provider, "provider is null"); + Objects.requireNonNull(tag, "tag is null"); + try { + NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl( + tag, provider, mAlertObserver, mStatsProviderCbList); + mStatsProviderCbList.register(callback); + Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid=" + + getCallingUid() + "/" + getCallingPid()); + return callback; + } catch (RemoteException e) { + Log.e(TAG, "registerNetworkStatsProvider failed", e); + } + return null; + } + + // Collect stats from local cache of providers. + private @NonNull NetworkStats getNetworkStatsFromProviders(int how) { + final NetworkStats ret = new NetworkStats(0L, 0); + invokeForAllStatsProviderCallbacks((cb) -> ret.combineAllValues(cb.getCachedStats(how))); + return ret; + } + + @FunctionalInterface + private interface ThrowingConsumer<S, T extends Throwable> { + void accept(S s) throws T; + } + + private void invokeForAllStatsProviderCallbacks( + @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) { + synchronized (mStatsProviderCbList) { + final int length = mStatsProviderCbList.beginBroadcast(); + try { + for (int i = 0; i < length; i++) { + final NetworkStatsProviderCallbackImpl cb = + mStatsProviderCbList.getBroadcastItem(i); + try { + task.accept(cb); + } catch (RemoteException e) { + Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e); + } + } + } finally { + mStatsProviderCbList.finishBroadcast(); + } + } + } + + private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub + implements IBinder.DeathRecipient { + @NonNull final String mTag; + @NonNull private final Object mProviderStatsLock = new Object(); + @NonNull final INetworkStatsProvider mProvider; + @NonNull final INetworkManagementEventObserver mAlertObserver; + @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList; + + @GuardedBy("mProviderStatsLock") + // STATS_PER_IFACE and STATS_PER_UID + private final NetworkStats mIfaceStats = new NetworkStats(0L, 0); + @GuardedBy("mProviderStatsLock") + private final NetworkStats mUidStats = new NetworkStats(0L, 0); + + NetworkStatsProviderCallbackImpl( + @NonNull String tag, @NonNull INetworkStatsProvider provider, + @NonNull INetworkManagementEventObserver alertObserver, + @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList) + throws RemoteException { + mTag = tag; + mProvider = provider; + mProvider.asBinder().linkToDeath(this, 0); + mAlertObserver = alertObserver; + mStatsProviderCbList = cbList; + } + + @NonNull + public NetworkStats getCachedStats(int how) { + synchronized (mProviderStatsLock) { + NetworkStats stats; + switch (how) { + case STATS_PER_IFACE: + stats = mIfaceStats; + break; + case STATS_PER_UID: + stats = mUidStats; + break; + default: + throw new IllegalArgumentException("Invalid type: " + how); + } + // Return a defensive copy instead of local reference. + return stats.clone(); + } + } + + @Override + public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats, + @Nullable NetworkStats uidStats) { + // TODO: 1. Use token to map ifaces to correct NetworkIdentity. + // 2. Store the difference and store it directly to the recorder. + synchronized (mProviderStatsLock) { + if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats); + if (uidStats != null) mUidStats.combineAllValues(uidStats); + } + } + + @Override + public void onAlertReached() throws RemoteException { + mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */); + } + + @Override + public void onLimitReached() { + Log.d(TAG, mTag + ": onLimitReached"); + LocalServices.getService(NetworkPolicyManagerInternal.class) + .onStatsProviderLimitReached(mTag); + } + + @Override + public void binderDied() { + Log.d(TAG, mTag + ": binderDied"); + mStatsProviderCbList.unregister(this); + } + + @Override + public void unregister() { + Log.d(TAG, mTag + ": unregister"); + mStatsProviderCbList.unregister(this); + } + + } + @VisibleForTesting static class HandlerCallback implements Handler.Callback { private final NetworkStatsService mService; @@ -1815,6 +1996,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS); } @Override + public long getPollDelay() { + return DEFAULT_PERFORM_POLL_DELAY_MS; + } + @Override public long getGlobalAlertBytes(long def) { return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def); } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index b782ca96ae88..3c31f6a7f0d7 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -83,6 +83,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -925,7 +926,7 @@ public final class OverlayManagerService extends SystemService { /** * Updates the target packages' set of enabled overlays in PackageManager. */ - private void updateOverlayPaths(int userId, List<String> targetPackageNames) { + private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) { try { traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames); if (DEBUG) { @@ -955,6 +956,7 @@ public final class OverlayManagerService extends SystemService { } } + final HashSet<String> updatedPackages = new HashSet<>(); final int n = targetPackageNames.size(); for (int i = 0; i < n; i++) { final String targetPackageName = targetPackageNames.get(i); @@ -965,11 +967,13 @@ public final class OverlayManagerService extends SystemService { } if (!pm.setEnabledOverlayPackages( - userId, targetPackageName, pendingChanges.get(targetPackageName))) { + userId, targetPackageName, pendingChanges.get(targetPackageName), + updatedPackages)) { Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", targetPackageName, userId)); } } + return new ArrayList<>(updatedPackages); } finally { traceEnd(TRACE_TAG_RRO); } @@ -980,10 +984,10 @@ public final class OverlayManagerService extends SystemService { } private void updateAssets(final int userId, List<String> targetPackageNames) { - updateOverlayPaths(userId, targetPackageNames); final IActivityManager am = ActivityManager.getService(); try { - am.scheduleApplicationInfoChanged(targetPackageNames, userId); + final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames); + am.scheduleApplicationInfoChanged(updatedPaths, userId); } catch (RemoteException e) { // Intentionally left empty. } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 019c9528f8ab..9623542a2900 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -685,7 +685,7 @@ final class OverlayManagerServiceImpl { // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. if (targetPackage != null && overlayPackage != null && !("android".equals(targetPackageName) - && overlayPackage.isStaticOverlayPackage())) { + && overlayPackage.isStaticOverlayPackage())) { mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } @@ -703,9 +703,9 @@ final class OverlayManagerServiceImpl { if (currentState != newState) { if (DEBUG) { Slog.d(TAG, String.format("%s:%d: %s -> %s", - overlayPackageName, userId, - OverlayInfo.stateToString(currentState), - OverlayInfo.stateToString(newState))); + overlayPackageName, userId, + OverlayInfo.stateToString(currentState), + OverlayInfo.stateToString(newState))); } modified |= mSettings.setState(overlayPackageName, userId, newState); } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index a009183f7ecb..28079099469a 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -55,7 +55,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -376,6 +375,33 @@ abstract class ApexManager { }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); } + private void populatePackageNameToApexModuleNameIfNeeded() { + synchronized (mLock) { + if (mPackageNameToApexModuleName != null) { + return; + } + try { + mPackageNameToApexModuleName = new ArrayMap<>(); + final ApexInfo[] allPkgs = mApexService.getAllPackages(); + for (int i = 0; i < allPkgs.length; i++) { + ApexInfo ai = allPkgs[i]; + PackageParser.PackageLite pkgLite; + try { + File apexFile = new File(ai.modulePath); + pkgLite = PackageParser.parsePackageLite(apexFile, 0); + } catch (PackageParser.PackageParserException pe) { + throw new IllegalStateException("Unable to parse: " + + ai.modulePath, pe); + } + mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName); + } + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re); + throw new RuntimeException(re); + } + } + } + private void populateAllPackagesCacheIfNeeded() { synchronized (mLock) { if (mAllPackagesCache != null) { @@ -383,7 +409,6 @@ abstract class ApexManager { } try { mAllPackagesCache = new ArrayList<>(); - mPackageNameToApexModuleName = new HashMap<>(); HashSet<String> activePackagesSet = new HashSet<>(); HashSet<String> factoryPackagesSet = new HashSet<>(); final ApexInfo[] allPkgs = mApexService.getAllPackages(); @@ -409,7 +434,6 @@ abstract class ApexManager { final PackageInfo packageInfo = PackageParser.generatePackageInfo(pkg, ai, flags); mAllPackagesCache.add(packageInfo); - mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); if (ai.isActive) { if (activePackagesSet.contains(packageInfo.packageName)) { throw new IllegalStateException( @@ -612,8 +636,7 @@ abstract class ApexManager { @Override List<String> getApksInApex(String apexPackageName) { - // TODO(b/142712057): Avoid calling populateAllPackagesCacheIfNeeded during boot. - populateAllPackagesCacheIfNeeded(); + populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { String moduleName = mPackageNameToApexModuleName.get(apexPackageName); if (moduleName == null) { diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index c4bcf809a67d..3e760962da87 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -449,6 +449,7 @@ public class AppsFilter { } final PackageSetting callingPkgSetting; final ArraySet<PackageSetting> callingSharedPkgSettings; + Trace.beginSection("callingSetting instanceof"); if (callingSetting instanceof PackageSetting) { callingPkgSetting = (PackageSetting) callingSetting; callingSharedPkgSettings = null; @@ -456,6 +457,7 @@ public class AppsFilter { callingPkgSetting = null; callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages; } + Trace.endSection(); if (callingPkgSetting != null) { if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) { @@ -485,6 +487,7 @@ public class AppsFilter { return true; } final String targetName = targetPkg.getPackageName(); + Trace.beginSection("getAppId"); final int callingAppId; if (callingPkgSetting != null) { callingAppId = callingPkgSetting.appId; @@ -492,6 +495,7 @@ public class AppsFilter { callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same } final int targetAppId = targetPkgSetting.appId; + Trace.endSection(); if (callingAppId == targetAppId) { if (DEBUG_LOGGING) { log(callingSetting, targetPkgSetting, "same app id"); @@ -499,38 +503,64 @@ public class AppsFilter { return false; } - if (callingSetting.getPermissionsState().hasPermission( - Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "has query-all permission"); + try { + Trace.beginSection("hasPermission"); + if (callingSetting.getPermissionsState().hasPermission( + Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "has query-all permission"); + } + return false; } - return false; + } finally { + Trace.endSection(); } - if (mForceQueryable.contains(targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "force queryable"); + try { + Trace.beginSection("mForceQueryable"); + if (mForceQueryable.contains(targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "force queryable"); + } + return false; } - return false; + } finally { + Trace.endSection(); } - if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { - // the calling package has explicitly declared the target package; allow - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries package"); + try { + Trace.beginSection("mQueriesViaPackage"); + if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { + // the calling package has explicitly declared the target package; allow + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries package"); + } + return false; } - return false; - } else if (mQueriesViaIntent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries intent"); + } finally { + Trace.endSection(); + } + try { + Trace.beginSection("mQueriesViaIntent"); + if (mQueriesViaIntent.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries intent"); + } + return false; } - return false; + } finally { + Trace.endSection(); } - final int targetUid = UserHandle.getUid(userId, targetAppId); - if (mImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + try { + Trace.beginSection("mImplicitlyQueryable"); + final int targetUid = UserHandle.getUid(userId, targetAppId); + if (mImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + } + return false; } - return false; + } finally { + Trace.endSection(); } if (callingPkgSetting != null) { if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) { @@ -576,17 +606,22 @@ public class AppsFilter { private static boolean callingPkgInstruments(PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, String targetName) { - final List<ComponentParseUtils.ParsedInstrumentation> inst = - callingPkgSetting.pkg.getInstrumentations(); - for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) { - if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) { - if (DEBUG_LOGGING) { - log(callingPkgSetting, targetPkgSetting, "instrumentation"); + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments"); + final List<ComponentParseUtils.ParsedInstrumentation> inst = + callingPkgSetting.pkg.getInstrumentations(); + for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) { + if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "instrumentation"); + } + return true; } - return true; } + return false; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return false; } private static void log(SettingBase callingPkgSetting, PackageSetting targetPkgSetting, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 17870ebe9957..dad32cd6c83e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -11602,6 +11602,23 @@ public class PackageManagerService extends IPackageManager.Stub return false; } SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx); + + // Remove the shared library overlays from its dependent packages. + for (int currentUserId : UserManagerService.getInstance().getUserIds()) { + final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr( + libraryInfo, 0, currentUserId); + if (dependents == null) { + continue; + } + for (VersionedPackage dependentPackage : dependents) { + final PackageSetting ps = mSettings.mPackages.get( + dependentPackage.getPackageName()); + if (ps != null) { + ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId); + } + } + } + versionedLib.remove(version); if (versionedLib.size() <= 0) { mSharedLibraries.remove(name); @@ -15210,6 +15227,29 @@ public class PackageManagerService extends IPackageManager.Stub // upcoming call to mSettings.writeLPr(). } } + + // Retrieve the overlays for shared libraries of the package. + if (pkg.getUsesLibraryInfos() != null) { + for (SharedLibraryInfo sharedLib : pkg.getUsesLibraryInfos()) { + for (int currentUserId : UserManagerService.getInstance().getUserIds()) { + if (!sharedLib.isDynamic()) { + // TODO(146804378): Support overlaying static shared libraries + continue; + } + final PackageSetting libPs = mSettings.mPackages.get( + sharedLib.getPackageName()); + if (libPs == null) { + continue; + } + final String[] overlayPaths = libPs.getOverlayPaths(currentUserId); + if (overlayPaths != null) { + ps.setOverlayPathsForLibrary(sharedLib.getName(), + Arrays.asList(overlayPaths), currentUserId); + } + } + } + } + // It's implied that when a user requests installation, they want the app to be // installed and enabled. if (userId != UserHandle.USER_ALL) { @@ -23249,9 +23289,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName, - @Nullable List<String> overlayPackageNames) { + @Nullable List<String> overlayPackageNames, + @NonNull Collection<String> outUpdatedPackageNames) { synchronized (mLock) { - if (targetPackageName == null || mPackages.get(targetPackageName) == null) { + final AndroidPackage targetPkg = mPackages.get(targetPackageName); + if (targetPackageName == null || targetPkg == null) { Slog.e(TAG, "failed to find package " + targetPackageName); return false; } @@ -23270,8 +23312,41 @@ public class PackageManagerService extends IPackageManager.Stub } } + ArraySet<String> updatedPackageNames = null; + if (targetPkg.getLibraryNames() != null) { + // Set the overlay paths for dependencies of the shared library. + updatedPackageNames = new ArraySet<>(); + for (String libName : targetPkg.getLibraryNames()) { + final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName, + SharedLibraryInfo.VERSION_UNDEFINED); + if (info == null) { + continue; + } + final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr( + info, 0, userId); + if (dependents == null) { + continue; + } + for (VersionedPackage dependent : dependents) { + final PackageSetting ps = mSettings.mPackages.get( + dependent.getPackageName()); + if (ps == null) { + continue; + } + ps.setOverlayPathsForLibrary(libName, overlayPaths, userId); + updatedPackageNames.add(dependent.getPackageName()); + } + } + } + final PackageSetting ps = mSettings.mPackages.get(targetPackageName); ps.setOverlayPaths(overlayPaths, userId); + + outUpdatedPackageNames.add(targetPackageName); + if (updatedPackageNames != null) { + outUpdatedPackageNames.addAll(updatedPackageNames); + } + return true; } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 0c0b93b18f84..f1ac0afa5dfd 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -41,6 +41,7 @@ import com.android.internal.util.Preconditions; import java.io.File; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -312,12 +313,21 @@ public abstract class PackageSettingBase extends SettingBase { } void setOverlayPaths(List<String> overlayPaths, int userId) { - modifyUserState(userId).overlayPaths = overlayPaths == null ? null : - overlayPaths.toArray(new String[overlayPaths.size()]); + modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null : + overlayPaths.toArray(new String[overlayPaths.size()])); } String[] getOverlayPaths(int userId) { - return readUserState(userId).overlayPaths; + return readUserState(userId).getOverlayPaths(); + } + + void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) { + modifyUserState(userId).setSharedLibraryOverlayPaths(libName, + overlayPaths == null ? null : overlayPaths.toArray(new String[0])); + } + + Map<String, String[]> getOverlayPathsForLibrary(int userId) { + return readUserState(userId).getSharedLibraryOverlayPaths(); } /** Only use for testing. Do NOT use in production code. */ diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index ec84b51577f9..3e665c324f0d 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4742,6 +4742,22 @@ public final class Settings { } } + Map<String, String[]> sharedLibraryOverlayPaths = + ps.getOverlayPathsForLibrary(user.id); + if (sharedLibraryOverlayPaths != null) { + for (Map.Entry<String, String[]> libOverlayPaths : + sharedLibraryOverlayPaths.entrySet()) { + if (libOverlayPaths.getValue() == null) { + continue; + } + pw.print(prefix); pw.print(" "); + pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:"); + for (String path : libOverlayPaths.getValue()) { + pw.print(prefix); pw.print(" "); pw.println(path); + } + } + } + String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); if (lastDisabledAppCaller != null) { pw.print(prefix); pw.print(" lastDisabledCaller: "); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 6d6ec250e4cc..46893b25de9a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -592,12 +592,6 @@ public final class DefaultPermissionGrantPolicy { getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId), userId, ALWAYS_LOCATION_PERMISSIONS); - // Gallery - grantPermissionsToSystemPackage( - getDefaultSystemHandlerActivityPackageForCategory( - Intent.CATEGORY_APP_GALLERY, userId), - userId, STORAGE_PERMISSIONS); - // Email grantPermissionsToSystemPackage( getDefaultSystemHandlerActivityPackageForCategory( diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS index 8d7f8822f78e..fc7fd220b26a 100644 --- a/services/core/java/com/android/server/stats/OWNERS +++ b/services/core/java/com/android/server/stats/OWNERS @@ -1,9 +1,8 @@ -bookatz@google.com -cjyu@google.com -dwchen@google.com +jeffreyhuang@google.com joeo@google.com +muhammadq@google.com +ruchirr@google.com singhtejinder@google.com -stlafon@google.com +tsaichristine@google.com yaochen@google.com -yanglu@google.com -yro@google.com
\ No newline at end of file +yro@google.com diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index f661b5e0d8a8..c96479543b3a 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; -import android.content.Intent; import android.os.TimestampedValue; import java.io.PrintWriter; @@ -73,9 +72,6 @@ public interface TimeDetectorStrategy { /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ void releaseWakeLock(); - - /** Send the supplied intent as a stick broadcast. */ - void sendStickyBroadcast(@NonNull Intent intent); } /** Initialize the strategy. */ diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java index 42d59d51c6af..9b89d9437fc3 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java @@ -20,11 +20,9 @@ import android.annotation.NonNull; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -112,11 +110,6 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat mWakeLock.release(); } - @Override - public void sendStickyBroadcast(@NonNull Intent intent) { - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - private void checkWakeLockHeld() { if (!mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held"); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index da848d87ba71..e95fc4a4a938 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -23,9 +23,7 @@ import android.app.AlarmManager; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; -import android.content.Intent; import android.os.TimestampedValue; -import android.telephony.TelephonyManager; import android.util.LocalLog; import android.util.Slog; @@ -535,17 +533,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } else { mLastAutoSystemClockTimeSet = null; } - - // Historically, Android has sent a TelephonyManager.ACTION_NETWORK_SET_TIME broadcast only - // when setting the time using NITZ. - if (origin == ORIGIN_PHONE) { - // Send a broadcast that telephony code used to send after setting the clock. - // TODO Remove this broadcast as soon as there are no remaining listeners. - Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_SET_TIME); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra("time", newSystemClockMillis); - mCallback.sendStickyBroadcast(intent); - } } /** diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 5b58199aec4f..eb7c5caa62aa 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -127,6 +127,9 @@ public final class TvInputManagerService extends SystemService { // A map from user id to UserState. private final SparseArray<UserState> mUserStates = new SparseArray<>(); + // A map from session id to session state saved in userstate + private final Map<String, SessionState> mSessionIdToSessionStateMap = new HashMap<>(); + private final WatchLogHandler mWatchLogHandler; public TvInputManagerService(Context context) { @@ -632,7 +635,8 @@ public final class TvInputManagerService extends SystemService { UserState userState = getOrCreateUserStateLocked(userId); SessionState sessionState = userState.sessionStateMap.get(sessionToken); if (DEBUG) { - Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")"); + Slog.d(TAG, "createSessionInternalLocked(inputId=" + + sessionState.inputId + ", sessionId=" + sessionState.sessionId + ")"); } InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); @@ -643,9 +647,11 @@ public final class TvInputManagerService extends SystemService { // Create a session. When failed, send a null token immediately. try { if (sessionState.isRecordingSession) { - service.createRecordingSession(callback, sessionState.inputId); + service.createRecordingSession( + callback, sessionState.inputId, sessionState.sessionId); } else { - service.createSession(channels[1], callback, sessionState.inputId); + service.createSession( + channels[1], callback, sessionState.inputId, sessionState.sessionId); } } catch (RemoteException e) { Slog.e(TAG, "error in createSession", e); @@ -715,6 +721,8 @@ public final class TvInputManagerService extends SystemService { } } + mSessionIdToSessionStateMap.remove(sessionState.sessionId); + ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName); if (serviceState != null) { serviceState.sessionTokens.remove(sessionToken); @@ -1156,9 +1164,11 @@ public final class TvInputManagerService extends SystemService { public void createSession(final ITvInputClient client, final String inputId, boolean isRecordingSession, int seq, int userId) { final int callingUid = Binder.getCallingUid(); - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "createSession"); final long identity = Binder.clearCallingIdentity(); + StringBuilder sessionId = new StringBuilder(); try { synchronized (mLock) { if (userId != mCurrentUserId && !isRecordingSession) { @@ -1187,15 +1197,21 @@ public final class TvInputManagerService extends SystemService { return; } + // Create a unique session id with pid, uid and resolved user id + sessionId.append(callingUid).append(callingPid).append(resolvedUserId); + // Create a new session token and a session state. IBinder sessionToken = new Binder(); SessionState sessionState = new SessionState(sessionToken, info.getId(), info.getComponent(), isRecordingSession, client, seq, callingUid, - resolvedUserId); + callingPid, resolvedUserId, sessionId.toString()); // Add them to the global session state map of the current user. userState.sessionStateMap.put(sessionToken, sessionState); + // Map the session id to the sessionStateMap in the user state + mSessionIdToSessionStateMap.put(sessionId.toString(), sessionState); + // Also, add them to the session state map of the current service. serviceState.sessionTokens.add(sessionToken); @@ -2003,6 +2019,43 @@ public final class TvInputManagerService extends SystemService { } @Override + public int getClientPid(String sessionId) { + ensureTunerResourceAccessPermission(); + final long identity = Binder.clearCallingIdentity(); + + int clientPid = TvInputManager.UNKNOWN_CLIENT_PID; + try { + synchronized (mLock) { + try { + clientPid = getClientPidLocked(sessionId); + } catch (ClientPidNotFoundException e) { + Slog.e(TAG, "error in getClientPid", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return clientPid; + } + + private int getClientPidLocked(String sessionId) + throws IllegalStateException { + if (mSessionIdToSessionStateMap.get(sessionId) == null) { + throw new IllegalStateException("Client Pid not found with sessionId " + + sessionId); + } + return mSessionIdToSessionStateMap.get(sessionId).callingPid; + } + + private void ensureTunerResourceAccessPermission() { + if (mContext.checkCallingPermission( + android.Manifest.permission.TUNER_RESOURCE_ACCESS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires TUNER_RESOURCE_ACCESS permission"); + } + } + + @Override @SuppressWarnings("resource") protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); @@ -2094,9 +2147,11 @@ public final class TvInputManagerService extends SystemService { pw.increaseIndent(); pw.println("inputId: " + session.inputId); + pw.println("sessionId: " + session.sessionId); pw.println("client: " + session.client); pw.println("seq: " + session.seq); pw.println("callingUid: " + session.callingUid); + pw.println("callingPid: " + session.callingPid); pw.println("userId: " + session.userId); pw.println("sessionToken: " + session.sessionToken); pw.println("session: " + session.session); @@ -2226,11 +2281,13 @@ public final class TvInputManagerService extends SystemService { private final class SessionState implements IBinder.DeathRecipient { private final String inputId; + private final String sessionId; private final ComponentName componentName; private final boolean isRecordingSession; private final ITvInputClient client; private final int seq; private final int callingUid; + private final int callingPid; private final int userId; private final IBinder sessionToken; private ITvInputSession session; @@ -2240,7 +2297,7 @@ public final class TvInputManagerService extends SystemService { private SessionState(IBinder sessionToken, String inputId, ComponentName componentName, boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, - int userId) { + int callingPid, int userId, String sessionId) { this.sessionToken = sessionToken; this.inputId = inputId; this.componentName = componentName; @@ -2248,7 +2305,9 @@ public final class TvInputManagerService extends SystemService { this.client = client; this.seq = seq; this.callingUid = callingUid; + this.callingPid = callingPid; this.userId = userId; + this.sessionId = sessionId; } @Override @@ -2962,4 +3021,10 @@ public final class TvInputManagerService extends SystemService { super(name); } } + + private static class ClientPidNotFoundException extends IllegalArgumentException { + public ClientPidNotFoundException(String name) { + super(name); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 26d76a8d6e28..320be2d7cdbd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5934,7 +5934,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAnimationBoundsLayer = createAnimationBoundsLayer(t); // Crop to stack bounds. - t.setWindowCrop(mAnimationBoundsLayer, mTmpRect); + if (!WindowManagerService.sHierarchicalAnimations) { + // For Hierarchical animation, we don't need to set window crop since the leash + // surface size has already same as the animating container. + t.setWindowCrop(mAnimationBoundsLayer, mTmpRect); + } t.setLayer(mAnimationBoundsLayer, layer); // Reparent leash to animation bounds layer. @@ -5982,9 +5986,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } clearThumbnail(); + final Transaction transaction = getAnimatingContainer().getPendingTransaction(); mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, - getPendingTransaction(), this, thumbnailHeader); - mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader)); + transaction, getAnimatingContainer(), thumbnailHeader); + mThumbnail.startAnimation(transaction, loadThumbnailAnimation(thumbnailHeader)); } /** @@ -6011,13 +6016,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (thumbnail == null) { return; } + final Transaction transaction = getAnimatingContainer().getPendingTransaction(); mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, - getPendingTransaction(), this, thumbnail); + transaction, getAnimatingContainer(), thumbnail); final Animation animation = getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked( win.getFrameLw()); - mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left, - frame.top)); + mThumbnail.startAnimation(transaction, animation, new Point(frame.left, frame.top)); } private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index f3880fa5dd09..c38868a2af84 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -42,6 +42,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.logWithStack; +import static com.android.server.wm.WindowManagerService.sHierarchicalAnimations; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import android.annotation.CallSuper; @@ -1855,7 +1856,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // TODO: Remove this and use #getBounds() instead once we set an app transition animation // on TaskStack. Rect getAnimationBounds(int appStackClipMode) { - return getBounds(); + return getDisplayedBounds(); } /** @@ -1929,7 +1930,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // Separate position and size for use in animators. mTmpRect.set(getAnimationBounds(appStackClipMode)); - mTmpPoint.set(mTmpRect.left, mTmpRect.top); + if (sHierarchicalAnimations) { + getRelativeDisplayedPosition(mTmpPoint); + } else { + mTmpPoint.set(mTmpRect.left, mTmpRect.top); + } mTmpRect.offsetTo(0, 0); final RemoteAnimationController controller = diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index acb6bea53a62..00436bb8ca70 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -204,6 +204,7 @@ struct GnssDeathRecipient : virtual public hidl_death_recipient // Must match the value from GnssMeasurement.java static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4); +static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4); sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr; sp<IGnss_V1_0> gnssHal = nullptr; @@ -634,6 +635,16 @@ private: template<class T> Return<void> gnssSvStatusCbImpl(const T& svStatus); + template<class T> + uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) { + return 0; + } + + template<class T> + double getBasebandCn0DbHz(const T& svStatus, size_t i) { + return 0.0; + } + uint32_t getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) { return svStatus.numSvs; } @@ -658,8 +669,6 @@ private: const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex( const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { - // TODO(b/144850155): fill baseband CN0 after it's available in Java object. - ALOGD("getGnssSvInfoOfIndex %d: baseband C/N0: %f", (int) i, svInfoList[i].basebandCN0DbHz); return svInfoList[i].v2_0.v1_0; } @@ -721,6 +730,18 @@ Return<void> GnssCallback::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValu return Void(); } +template<> +uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& + svStatus) { + return SVID_FLAGS_HAS_BASEBAND_CN0; +} + +template<> +double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, + size_t i) { + return svInfoList[i].basebandCN0DbHz; +} + template<class T> Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) { JNIEnv* env = getJniEnv(); @@ -758,8 +779,8 @@ Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) { elev[i] = info.elevationDegrees; azim[i] = info.azimuthDegrees; carrierFreq[i] = info.carrierFrequencyHz; - // TODO(b/144850155): fill svidWithFlags with hasBasebandCn0DbHz based on HAL versions - basebandCn0s[i] = 0.0; + svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus); + basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i); } env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0); @@ -1185,8 +1206,8 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement const IGnssMeasurementCallback_V2_1::GnssMeasurement* measurement_V2_1, JavaObject& object) { translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object); - // TODO(b/144850155): fill baseband CN0 after it's available in Java object - ALOGD("baseband CN0DbHz = %f\n", measurement_V2_1->basebandCN0DbHz); + + SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz); } template<class T> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3592e5f1200a..6b81fdd2d270 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -129,6 +129,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DeviceStateCache; +import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.NetworkEvent; import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; @@ -268,6 +269,7 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.PasswordValidationError; import com.android.server.LocalServices; import com.android.server.LockGuard; +import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo; @@ -1006,6 +1008,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL = "cross-profile-calendar-packages-null"; private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages"; + private static final String TAG_FACTORY_RESET_PROTECTION_POLICY = + "factory_reset_protection_policy"; DeviceAdminInfo info; @@ -1016,6 +1020,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @NonNull PasswordPolicy mPasswordPolicy = new PasswordPolicy(); + @Nullable + FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null; + static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0; long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK; @@ -1351,6 +1358,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mCrossProfileCalendarPackages); } writePackageListToXml(out, TAG_CROSS_PROFILE_PACKAGES, mCrossProfilePackages); + if (mFactoryResetProtectionPolicy != null) { + out.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY); + mFactoryResetProtectionPolicy.writeToXml(out); + out.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY); + } } void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException { @@ -1584,6 +1596,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mCrossProfileCalendarPackages = null; } else if (TAG_CROSS_PROFILE_PACKAGES.equals(tag)) { mCrossProfilePackages = readPackageList(parser, tag); + } else if (TAG_FACTORY_RESET_PROTECTION_POLICY.equals(tag)) { + mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml( + parser); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -2036,6 +2051,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE)); } + PersistentDataBlockManagerInternal getPersistentDataBlockManagerInternal() { + return LocalServices.getService(PersistentDataBlockManagerInternal.class); + } + LockSettingsInternal getLockSettingsInternal() { return LocalServices.getService(LockSettingsInternal.class); } @@ -6736,6 +6755,67 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public void setFactoryResetProtectionPolicy(ComponentName who, + @Nullable FactoryResetProtectionPolicy policy) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + + final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow(); + final int userId = mInjector.userHandleGetCallingUserId(); + synchronized (getLockObject()) { + ActiveAdmin admin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); + admin.mFactoryResetProtectionPolicy = policy; + saveSettingsLocked(userId); + } + + mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser( + new Intent(DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED), + UserHandle.getUserHandleForUid(frpManagementAgentUid))); + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_FACTORY_RESET_PROTECTION) + .setAdmin(who) + .write(); + } + + @Override + public FactoryResetProtectionPolicy getFactoryResetProtectionPolicy( + @Nullable ComponentName who) { + if (!mHasFeature) { + return null; + } + + final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow(); + ActiveAdmin admin; + synchronized (getLockObject()) { + if (who == null) { + if ((frpManagementAgentUid != mInjector.binderGetCallingUid())) { + throw new SecurityException( + "Must be called by the FRP management agent on device"); + } + admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( + UserHandle.getUserId(frpManagementAgentUid)); + } else { + admin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); + } + } + return admin != null ? admin.mFactoryResetProtectionPolicy : null; + } + + private int getFrpManagementAgentUidOrThrow() { + PersistentDataBlockManagerInternal pdb = mInjector.getPersistentDataBlockManagerInternal(); + if ((pdb == null) || (pdb.getAllowedUid() == -1)) { + throw new UnsupportedOperationException( + "The persistent data block service is not supported on this device"); + } + return pdb.getAllowedUid(); + } + + @Override public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) { if (!mHasFeature) { return; @@ -8131,6 +8211,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } + ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(int userId) { + ActiveAdmin admin = getDeviceOwnerAdminLocked(); + if (admin == null) { + admin = getProfileOwnerOfOrganizationOwnedDeviceLocked(userId); + } + return admin; + } + @Override public void clearDeviceOwner(String packageName) { Objects.requireNonNull(packageName, "packageName is null"); diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java index a8a258fc5ff7..9c5d4ad6ceeb 100644 --- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java +++ b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java @@ -3,6 +3,7 @@ package com.android.server.location; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import android.os.Looper; import android.os.SystemClock; @@ -52,8 +53,10 @@ public class NtpTimeHelperTest { @Test public void handleInjectNtpTime_cachedAgeLow_injectTime() throws InterruptedException { - doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(mMockNtpTrustedTime).getCacheAge(); - doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime(); + NtpTrustedTime.TimeResult result = mock(NtpTrustedTime.TimeResult.class); + doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis(); + doReturn(MOCK_NTP_TIME).when(result).getTimeMillis(); + doReturn(result).when(mMockNtpTrustedTime).getCachedTimeResult(); mNtpTimeHelper.retrieveAndInjectNtpTime(); @@ -64,7 +67,9 @@ public class NtpTimeHelperTest { @Test public void handleInjectNtpTime_injectTimeFailed_injectTimeDelayed() throws InterruptedException { - doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(mMockNtpTrustedTime).getCacheAge(); + NtpTrustedTime.TimeResult result1 = mock(NtpTrustedTime.TimeResult.class); + doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis(); + doReturn(result1).when(mMockNtpTrustedTime).getCachedTimeResult(); doReturn(false).when(mMockNtpTrustedTime).forceRefresh(); mNtpTimeHelper.retrieveAndInjectNtpTime(); @@ -72,8 +77,10 @@ public class NtpTimeHelperTest { assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse(); doReturn(true).when(mMockNtpTrustedTime).forceRefresh(); - doReturn(1L).when(mMockNtpTrustedTime).getCacheAge(); - doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime(); + NtpTrustedTime.TimeResult result2 = mock(NtpTrustedTime.TimeResult.class); + doReturn(1L).when(result2).getAgeMillis(); + doReturn(MOCK_NTP_TIME).when(result2).getTimeMillis(); + doReturn(result2).when(mMockNtpTrustedTime).getCachedTimeResult(); SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL); waitForTasksToBePostedOnHandlerAndRunThem(); diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index 556f96ace5d2..6a5de84266e2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -345,8 +345,8 @@ public class AlarmManagerServiceTest { } /** - * Lowers quotas to make testing feasible. - * Careful while calling as this will replace any existing settings for the calling test. + * Lowers quotas to make testing feasible. Careful while calling as this will replace any + * existing settings for the calling test. */ private void setTestableQuotas() { final StringBuilder constantsBuilder = new StringBuilder(); @@ -981,6 +981,25 @@ public class AlarmManagerServiceTest { assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); } + @Test + public void alarmCountOnListenerBinderDied() { + final int numAlarms = 10; + final IAlarmListener[] listeners = new IAlarmListener[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + listeners[i] = new IAlarmListener.Stub() { + @Override + public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { + } + }; + setTestAlarmWithListener(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i, listeners[i]); + } + assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); + for (int i = 0; i < numAlarms; i++) { + mService.mListenerDeathRecipient.binderDied(listeners[i].asBinder()); + assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); + } + } + @After public void tearDown() { if (mMockingSession != null) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 591c3a385e23..c223f135d3df 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -804,6 +804,11 @@ public class AbstractAccessibilityServiceConnectionTest { } @Override + public boolean switchToInputMethod(String imeId) { + return false; + } + + @Override public boolean isAccessibilityButtonAvailable() throws RemoteException { return false; } diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java index d44476ed971d..3de006cea15f 100644 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -57,7 +57,6 @@ import com.android.server.backup.utils.RandomAccessFileUtils; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -523,7 +522,6 @@ public class BackupManagerServiceTest { * Test that {@link BackupManagerService#dump()} dumps system user information before non-system * user information. */ - @Test public void testDump_systemUserFirst() { String[] args = new String[0]; @@ -539,16 +537,31 @@ public class BackupManagerServiceTest { } @Test - @Ignore("b/147012496") - public void testGetUserForAncestralSerialNumber() { + public void testGetUserForAncestralSerialNumber_forSystemUser() { BackupManagerServiceTestable.sBackupDisabled = false; BackupManagerService backupManagerService = new BackupManagerServiceTestable(mContextMock, mUserServices); + when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false)) + .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM}); when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L); - assertThat(user).isEqualTo(UserHandle.of(1)); + assertThat(user).isEqualTo(UserHandle.of(UserHandle.USER_SYSTEM)); + } + + @Test + public void testGetUserForAncestralSerialNumber_forNonSystemUser() { + BackupManagerServiceTestable.sBackupDisabled = false; + BackupManagerService backupManagerService = + new BackupManagerServiceTestable(mContextMock, mUserServices); + when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false)) + .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM}); + when(mNonSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); + + UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L); + + assertThat(user).isEqualTo(UserHandle.of(NON_USER_SYSTEM)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index ac555fda2204..3a8258be5f01 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -51,6 +51,7 @@ import androidx.annotation.NonNull; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; +import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import java.io.File; @@ -223,6 +224,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override + PersistentDataBlockManagerInternal getPersistentDataBlockManagerInternal() { + return services.persistentDataBlockManagerInternal; + } + + @Override Looper getMyLooper() { return Looper.getMainLooper(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 20cb4497e08f..21034d3de9b4 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -31,6 +31,8 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; import static com.android.server.testutils.TestUtils.assertExpectException; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; @@ -62,6 +64,7 @@ import android.app.Notification; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; +import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.PasswordMetrics; import android.app.timedetector.ManualTimeSuggestion; import android.app.timezonedetector.ManualTimeZoneSuggestion; @@ -2022,6 +2025,116 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); } + public void testSetFactoryResetProtectionPolicyWithDO() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + + when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn( + DpmMockContext.CALLER_UID); + + FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionAccounts(new ArrayList<>()) + .setFactoryResetProtectionDisabled(true) + .build(); + dpm.setFactoryResetProtectionPolicy(admin1, policy); + + FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(admin1); + assertThat(result).isEqualTo(policy); + assertPoliciesAreEqual(policy, result); + + verify(mContext.spiedContext).sendBroadcastAsUser( + MockUtils.checkIntentAction( + DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED), + MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE)); + } + + public void testSetFactoryResetProtectionPolicyFailWithPO() throws Exception { + setupProfileOwner(); + + FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionDisabled(true) + .build(); + + assertExpectException(SecurityException.class, null, + () -> dpm.setFactoryResetProtectionPolicy(admin1, policy)); + } + + public void testSetFactoryResetProtectionPolicyWithPOOfOrganizationOwnedDevice() + throws Exception { + setupProfileOwner(); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); + + when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn( + DpmMockContext.CALLER_UID); + + List<String> accounts = new ArrayList<>(); + accounts.add("Account 1"); + accounts.add("Account 2"); + + FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionAccounts(accounts) + .build(); + + dpm.setFactoryResetProtectionPolicy(admin1, policy); + + FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(admin1); + assertThat(result).isEqualTo(policy); + assertPoliciesAreEqual(policy, result); + + verify(mContext.spiedContext, times(2)).sendBroadcastAsUser( + MockUtils.checkIntentAction( + DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), + MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE)); + verify(mContext.spiedContext).sendBroadcastAsUser( + MockUtils.checkIntentAction( + DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED), + MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE)); + verify(mContext.spiedContext).sendBroadcastAsUser( + MockUtils.checkIntentAction( + DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED), + MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE)); + } + + public void testGetFactoryResetProtectionPolicyWithFrpManagementAgent() + throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn( + DpmMockContext.CALLER_UID); + + FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionAccounts(new ArrayList<>()) + .setFactoryResetProtectionDisabled(true) + .build(); + + dpm.setFactoryResetProtectionPolicy(admin1, policy); + + mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + dpm.setActiveAdmin(admin1, /*replace=*/ false); + FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(null); + assertThat(result).isEqualTo(policy); + assertPoliciesAreEqual(policy, result); + + verify(mContext.spiedContext).sendBroadcastAsUser( + MockUtils.checkIntentAction( + DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED), + MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE)); + } + + private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy, + FactoryResetProtectionPolicy actualPolicy) { + assertThat(actualPolicy.isFactoryResetProtectionDisabled()).isEqualTo( + expectedPolicy.isFactoryResetProtectionDisabled()); + assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(), + actualPolicy.getFactoryResetProtectionAccounts()); + } + + private void assertAccountsAreEqual(List<String> expectedAccounts, + List<String> actualAccounts) { + assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts); + } + public void testGetMacAddress() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java new file mode 100644 index 000000000000..bc853c693b3a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java @@ -0,0 +1,150 @@ +/* + * 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.devicepolicy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.admin.FactoryResetProtectionPolicy; +import android.os.Parcel; +import android.util.Xml; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link android.app.admin.FactoryResetProtectionPolicy}. + * + * atest com.android.server.devicepolicy.FactoryResetProtectionPolicyTest + */ +@RunWith(AndroidJUnit4.class) +public class FactoryResetProtectionPolicyTest { + + private static final String TAG_FACTORY_RESET_PROTECTION_POLICY = + "factory_reset_protection_policy"; + + @Test + public void testNonDefaultFactoryResetProtectionPolicyObject() throws Exception { + List<String> accounts = new ArrayList<>(); + accounts.add("Account 1"); + accounts.add("Account 2"); + + FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionAccounts(accounts) + .setFactoryResetProtectionDisabled(true) + .build(); + + testParcelAndUnparcel(policy); + testSerializationAndDeserialization(policy); + } + + @Test + public void testInvalidXmlFactoryResetProtectionPolicyObject() throws Exception { + List<String> accounts = new ArrayList<>(); + accounts.add("Account 1"); + accounts.add("Account 2"); + + FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionAccounts(accounts) + .setFactoryResetProtectionDisabled(true) + .build(); + + testParcelAndUnparcel(policy); + testInvalidXmlSerializationAndDeserialization(policy); + } + + private void testParcelAndUnparcel(FactoryResetProtectionPolicy policy) { + Parcel parcel = Parcel.obtain(); + policy.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + FactoryResetProtectionPolicy actualPolicy = + FactoryResetProtectionPolicy.CREATOR.createFromParcel(parcel); + assertPoliciesAreEqual(policy, actualPolicy); + parcel.recycle(); + } + + private void testSerializationAndDeserialization(FactoryResetProtectionPolicy policy) + throws Exception { + ByteArrayOutputStream outStream = serialize(policy); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new InputStreamReader(inStream)); + assertEquals(XmlPullParser.START_TAG, parser.next()); + + assertPoliciesAreEqual(policy, policy.readFromXml(parser)); + } + + private void testInvalidXmlSerializationAndDeserialization(FactoryResetProtectionPolicy policy) + throws Exception { + ByteArrayOutputStream outStream = serialize(policy); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + XmlPullParser parser = mock(XmlPullParser.class); + when(parser.next()).thenThrow(XmlPullParserException.class); + parser.setInput(new InputStreamReader(inStream)); + + // If deserialization fails, then null is returned. + assertNull(policy.readFromXml(parser)); + } + + private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy) + throws IOException { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + final XmlSerializer outXml = new FastXmlSerializer(); + outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); + outXml.startDocument(null, true); + outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY); + policy.writeToXml(outXml); + outXml.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY); + outXml.endDocument(); + outXml.flush(); + return outStream; + } + + private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy, + FactoryResetProtectionPolicy actualPolicy) { + assertEquals(expectedPolicy.isFactoryResetProtectionDisabled(), + actualPolicy.isFactoryResetProtectionDisabled()); + assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(), + actualPolicy.getFactoryResetProtectionAccounts()); + } + + private void assertAccountsAreEqual(List<String> expectedAccounts, + List<String> actualAccounts) { + assertEquals(expectedAccounts.size(), actualAccounts.size()); + for (int i = 0; i < expectedAccounts.size(); i++) { + assertEquals(expectedAccounts.get(i), actualAccounts.get(i)); + } + } + +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 919a3f6d7d1b..6c2c1446a459 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -68,6 +68,7 @@ import android.view.IWindowManager; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; +import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -117,6 +118,7 @@ public class MockSystemServices { public final TimeDetector timeDetector; public final TimeZoneDetector timeZoneDetector; public final KeyChain.KeyChainConnection keyChainConnection; + public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; public final BuildMock buildMock = new BuildMock(); @@ -160,6 +162,7 @@ public class MockSystemServices { timeDetector = mock(TimeDetector.class); timeZoneDetector = mock(TimeZoneDetector.class); keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS); + persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class); // Package manager is huge, so we use a partial mock instead. packageManager = spy(realContext.getPackageManager()); diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java index 63189e7546e2..5aed194773f5 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java @@ -16,14 +16,7 @@ package com.android.server.integrity; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasItems; -import static org.junit.Assert.assertThat; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.content.integrity.AppInstallMetadata; import android.content.integrity.AtomicFormula; @@ -33,26 +26,26 @@ import android.content.integrity.CompoundFormula; import android.content.integrity.Rule; import android.util.Slog; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.integrity.parser.RuleXmlParser; -import com.android.server.integrity.serializer.RuleXmlSerializer; +import com.android.server.integrity.parser.RuleBinaryParser; +import com.android.server.integrity.serializer.RuleBinarySerializer; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; /** Unit test for {@link IntegrityFileManager} */ -@RunWith(AndroidJUnit4.class) +@RunWith(JUnit4.class) public class IntegrityFileManagerTest { private static final String TAG = "IntegrityFileManagerTest"; @@ -72,7 +65,7 @@ public class IntegrityFileManagerTest { // Use Xml Parser/Serializer to help with debugging since we can just print the file. mIntegrityFileManager = new IntegrityFileManager( - new RuleXmlParser(), new RuleXmlSerializer(), mTmpDir); + new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir); Files.walk(mTmpDir.toPath()) .forEach( path -> { @@ -97,12 +90,19 @@ public class IntegrityFileManagerTest { @Test public void testGetMetadata() throws Exception { - assertNull(mIntegrityFileManager.readMetadata()); + assertThat(mIntegrityFileManager.readMetadata()).isNull(); mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - assertNotNull(mIntegrityFileManager.readMetadata()); - assertEquals(VERSION, mIntegrityFileManager.readMetadata().getVersion()); - assertEquals(RULE_PROVIDER, mIntegrityFileManager.readMetadata().getRuleProvider()); + assertThat(mIntegrityFileManager.readMetadata()).isNotNull(); + assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION); + assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER); + } + + @Test + public void testIsInitialized() throws Exception { + assertThat(mIntegrityFileManager.initialized()).isFalse(); + mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); + assertThat(mIntegrityFileManager.initialized()).isTrue(); } @Test @@ -110,20 +110,8 @@ public class IntegrityFileManagerTest { String packageName = "package"; String packageCert = "cert"; int version = 123; - Rule packageNameRule = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - Rule.DENY); - Rule packageCertRule = - new Rule( - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - packageCert, - /* isHashedValue= */ false), - Rule.DENY); + Rule packageNameRule = getPackageNameIndexedRule(packageName); + Rule packageCertRule = getAppCertificateIndexedRule(packageCert); Rule versionCodeRule = new Rule( new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, version), @@ -142,9 +130,7 @@ public class IntegrityFileManagerTest { AtomicFormula.LE, version))), Rule.DENY); - // We will test the specifics of indexing in other classes. Here, we just require that all - // rules that are related to the given AppInstallMetadata are returned and do not assert - // anything on other rules. + List<Rule> rules = Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule); mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); @@ -159,17 +145,77 @@ public class IntegrityFileManagerTest { .build(); List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); - assertThat(rulesFetched, hasItems( - equalTo(packageNameRule), - equalTo(packageCertRule), - equalTo(versionCodeRule) - )); + assertThat(rulesFetched) + .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule); } @Test - public void testIsInitialized() throws Exception { - assertFalse(mIntegrityFileManager.initialized()); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - assertTrue(mIntegrityFileManager.initialized()); + public void testGetRules_indexedForManyRules() throws Exception { + String packageName = "package"; + String installerName = "installer"; + String appCertificate = "cert"; + + // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and + // 500 unindexed rules. + List<Rule> rules = new ArrayList<>(); + + for (int i = 0; i < 2500; i++) { + rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i))); + rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i))); + } + + for (int i = 0; i < 70; i++) { + rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i))); + } + + // Write the rules and get them indexed. + mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); + + // Read the rules for a specific rule. + String installedPackageName = String.format("%s%04d", packageName, 264); + String installedAppCertificate = String.format("%s%04d", appCertificate, 1264); + AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder() + .setPackageName(installedPackageName) + .setAppCertificate(installedAppCertificate) + .setVersionCode(250) + .setInstallerName("abc") + .setInstallerCertificate("abc") + .setIsPreInstalled(true) + .build(); + List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); + + // Verify that we do not load all the rules and we have the necessary rules to evaluate. + assertThat(rulesFetched.size()).isEqualTo(270); + assertThat(rulesFetched) + .containsAllOf( + getPackageNameIndexedRule(installedPackageName), + getAppCertificateIndexedRule(installedAppCertificate)); + } + + private Rule getPackageNameIndexedRule(String packageName) { + return new Rule( + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + packageName, + /* isHashedValue= */ false), + Rule.DENY); + } + + private Rule getAppCertificateIndexedRule(String appCertificate) { + return new Rule( + new StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE, + appCertificate, + /* isHashedValue= */ false), + Rule.DENY); + } + + private Rule getInstallerCertificateRule(String installerCert) { + return new Rule( + new StringAtomicFormula( + AtomicFormula.INSTALLER_NAME, + installerCert, + /* isHashedValue= */ false), + Rule.DENY); } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java index 1eb5eb51504a..4fa73028ece1 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java @@ -121,6 +121,26 @@ public class BitTrackedInputStreamTest { } @Test + public void testBitTrackedInputStream_canReadMoreRules() throws IOException { + String packageName = "com.test.app"; + byte[] testInput = + getBytes( + IS_NOT_HASHED + + getBits(packageName.length(), VALUE_SIZE_BITS) + + getValueBits(packageName)); + + BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput); + assertThat(bitTrackedInputStream.canReadMoreRules(2)).isTrue(); + + // Read until the string parameter. + String stringValue = BinaryFileOperations.getStringValue(bitTrackedInputStream); + + // Verify that the read bytes are counted. + assertThat(stringValue).isEqualTo(packageName); + assertThat(bitTrackedInputStream.canReadMoreRules(2)).isFalse(); + } + + @Test public void testBitTrackedInputStream_moveCursorForwardFailsIfAlreadyRead() throws IOException { String packageName = "com.test.app"; byte[] testInput = diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index 51f5c755754c..881b3d6bba52 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -48,7 +48,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -656,65 +655,4 @@ public class RuleBinaryParserTest { /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit", () -> binaryParser.parse(rule.array())); } - - @Test - public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly() - throws Exception { - String packageName2 = "com.test.2"; - - byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1")); - byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2)); - byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3")); - - ByteBuffer rule = - ByteBuffer.allocate( - DEFAULT_FORMAT_VERSION_BYTES.length - + ruleBytes1.length - + ruleBytes2.length - + ruleBytes3.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes1); - rule.put(ruleBytes2); - rule.put(ruleBytes3); - InputStream inputStream = new ByteArrayInputStream(rule.array()); - - RuleParser binaryParser = new RuleBinaryParser(); - - List<RuleIndexRange> indexRanges = new ArrayList<>(); - indexRanges.add( - new RuleIndexRange( - DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length, - DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length - + ruleBytes2.length)); - List<Rule> rules = binaryParser.parse(inputStream, indexRanges); - - Rule expectedRule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName2, - /* isHashedValue= */ false))), - Rule.DENY); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - private static String getRulesWithPackageName(String packageName) { - return START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - - } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java index 742952e056bc..291cfeea5bbc 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java @@ -115,7 +115,8 @@ public class RuleIndexingControllerTest { + getKeyValueString(START_INDEXING_KEY, 500) + getKeyValueString(END_INDEXING_KEY, 900) + getKeyValueString(START_INDEXING_KEY, 900) - + getKeyValueString(END_INDEXING_KEY, 945)); + + getKeyValueString(END_INDEXING_KEY, 945) + + getBits(1, 1)); ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); rule.put(stringBytes); InputStream inputStream = new ByteArrayInputStream(rule.array()); @@ -152,7 +153,8 @@ public class RuleIndexingControllerTest { + getKeyValueString("888", 800) + getKeyValueString(END_INDEXING_KEY, 900) + getKeyValueString(START_INDEXING_KEY, 900) - + getKeyValueString(END_INDEXING_KEY, 945)); + + getKeyValueString(END_INDEXING_KEY, 945) + + getBits(1, 1)); ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); rule.put(stringBytes); return new ByteArrayInputStream(rule.array()); diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java index eb6698b0d479..bc2aac0acf10 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java @@ -135,15 +135,14 @@ public class RuleBinarySerializerTest { .isEqualTo(expectedRuleOutputStream.toByteArray()); ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream(); + String serializedIndexingBytes = + SERIALIZED_START_INDEXING_KEY + + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) + + SERIALIZED_END_INDEXING_KEY + + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */32); byte[] expectedIndexingBytes = - getBytes( - SERIALIZED_START_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) - + SERIALIZED_END_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ - 32)); - expectedIndexingOutputStream.write(expectedIndexingBytes); - expectedIndexingOutputStream.write(expectedIndexingBytes); + getBytes(serializedIndexingBytes + serializedIndexingBytes + + serializedIndexingBytes + getBits(1, 1)); expectedIndexingOutputStream.write(expectedIndexingBytes); assertThat(indexingOutputStream.toByteArray()) .isEqualTo(expectedIndexingOutputStream.toByteArray()); @@ -197,15 +196,16 @@ public class RuleBinarySerializerTest { + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) + SERIALIZED_END_INDEXING_KEY + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32); - expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed)); - expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed)); String expectedIndexingBitsForUnindexed = SERIALIZED_START_INDEXING_KEY + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) + SERIALIZED_END_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length + getBytes( - expectedBits).length, /* numOfBits= */ 32); - expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForUnindexed)); + + getBits(DEFAULT_FORMAT_VERSION_BYTES.length + + getBytes(expectedBits).length, /* numOfBits= */ 32); + expectedIndexingOutputStream.write(getBytes( + expectedIndexingBitsForIndexed + expectedIndexingBitsForIndexed + + expectedIndexingBitsForUnindexed + getBits(1, 1))); + assertThat(indexingOutputStream.toByteArray()) .isEqualTo(expectedIndexingOutputStream.toByteArray()); } @@ -564,7 +564,6 @@ public class RuleBinarySerializerTest { expectedIndexingBytesForPackageNameIndexed += SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForPackageNameIndexed)); String expectedIndexingBytesForAppCertificateIndexed = SERIALIZED_START_INDEXING_KEY @@ -588,7 +587,6 @@ public class RuleBinarySerializerTest { expectedIndexingBytesForAppCertificateIndexed += SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForAppCertificateIndexed)); String expectedIndexingBytesForUnindexed = SERIALIZED_START_INDEXING_KEY @@ -603,8 +601,11 @@ public class RuleBinarySerializerTest { expectedIndexingBytesForUnindexed += SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForUnindexed)); - + expectedIndexingOutputStream.write( + getBytes(expectedIndexingBytesForPackageNameIndexed + + expectedIndexingBytesForAppCertificateIndexed + + expectedIndexingBytesForUnindexed + + getBits(1, 1))); assertThat(ruleOutputStream.toByteArray()) .isEqualTo(expectedOrderedRuleOutputStream.toByteArray()); diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java index 55fada44b99e..1674422f3af9 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java @@ -38,10 +38,8 @@ import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.TreeMap; /** Unit tests for {@link RuleIndexingDetailsIdentifier}. */ @RunWith(JUnit4.class) @@ -140,7 +138,7 @@ public class RuleIndexingDetailsIdentifierTest { List<Rule> ruleList = new ArrayList(); ruleList.add(RULE_WITH_PACKAGE_NAME); - Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); + Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); // Verify the resulting map content. assertThat(result.keySet()) @@ -157,7 +155,7 @@ public class RuleIndexingDetailsIdentifierTest { List<Rule> ruleList = new ArrayList(); ruleList.add(RULE_WITH_APP_CERTIFICATE); - Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); + Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); assertThat(result.keySet()) .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); @@ -174,7 +172,7 @@ public class RuleIndexingDetailsIdentifierTest { List<Rule> ruleList = new ArrayList(); ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS); - Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); + Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); assertThat(result.keySet()) .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); @@ -189,7 +187,7 @@ public class RuleIndexingDetailsIdentifierTest { List<Rule> ruleList = new ArrayList(); ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS); - Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); + Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); assertThat(result.keySet()) .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); @@ -215,7 +213,7 @@ public class RuleIndexingDetailsIdentifierTest { List<Rule> ruleList = new ArrayList(); ruleList.add(negatedRule); - Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); + Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); assertThat(result.keySet()) .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); @@ -225,7 +223,7 @@ public class RuleIndexingDetailsIdentifierTest { } @Test - public void getIndexType_allRulesTogetherInValidOrder() { + public void getIndexType_allRulesTogetherSplitCorrectly() { Rule packageNameRuleA = getRuleWithPackageName("aaa"); Rule packageNameRuleB = getRuleWithPackageName("bbb"); Rule packageNameRuleC = getRuleWithPackageName("ccc"); @@ -243,38 +241,20 @@ public class RuleIndexingDetailsIdentifierTest { ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS); ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS); - Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); + Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); assertThat(result.keySet()) .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); // We check asserts this way to ensure ordering based on package name. assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly("aaa", "bbb", "ccc"); - Iterator<String> keySetIterator = result.get(PACKAGE_NAME_INDEXED).keySet().iterator(); - assertThat(keySetIterator.next()).isEqualTo("aaa"); - assertThat(keySetIterator.next()).isEqualTo("bbb"); - assertThat(keySetIterator.next()).isEqualTo("ccc"); - assertThat(result.get(PACKAGE_NAME_INDEXED).get("aaa")).containsExactly(packageNameRuleA); - assertThat(result.get(PACKAGE_NAME_INDEXED).get("bbb")).containsExactly(packageNameRuleB); - assertThat(result.get(PACKAGE_NAME_INDEXED).get("ccc")).containsExactly(packageNameRuleC); // We check asserts this way to ensure ordering based on app certificate. assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()).containsExactly("cert1", "cert2", "cert3"); - keySetIterator = result.get(APP_CERTIFICATE_INDEXED).keySet().iterator(); - assertThat(keySetIterator.next()).isEqualTo("cert1"); - assertThat(keySetIterator.next()).isEqualTo("cert2"); - assertThat(keySetIterator.next()).isEqualTo("cert3"); - assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert1")).containsExactly( - certificateRule1); - assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert2")).containsExactly( - certificateRule2); - assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert3")).containsExactly( - certificateRule3); assertThat(result.get(NOT_INDEXED).get("N/A")) - .containsExactly( - RULE_WITH_INSTALLER_RESTRICTIONS, + .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS, RULE_WITH_NONSTRING_RESTRICTIONS); } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index c7dbad83e384..e1c489e65984 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -37,6 +37,7 @@ import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.TAG_ALL; +import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.TrafficStats.MB_IN_BYTES; @@ -1758,6 +1759,57 @@ public class NetworkPolicyManagerServiceTest { } /** + * Test that when StatsProvider triggers limit reached, new limit will be calculated and + * re-armed. + */ + @Test + public void testStatsProviderLimitReached() throws Exception { + final int CYCLE_DAY = 15; + + final NetworkStats stats = new NetworkStats(0L, 1); + stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE, + 2999, 1, 2000, 1, 0); + when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) + .thenReturn(stats.getTotalBytes()); + when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) + .thenReturn(stats); + + // Get active mobile network in place + expectMobileDefaults(); + mService.updateNetworks(); + verify(mStatsService).setStatsProviderLimit(TEST_IFACE, Long.MAX_VALUE); + + // Set limit to 10KB. + setNetworkPolicies(new NetworkPolicy( + sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, 10000L, + true)); + postMsgAndWaitForCompletion(); + + // Verifies that remaining quota is set to providers. + verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L); + + reset(mStatsService); + + // Increase the usage. + stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE, + 1000, 1, 999, 1, 0); + when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) + .thenReturn(stats.getTotalBytes()); + when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) + .thenReturn(stats); + + // Simulates that limit reached fires earlier by provider, but actually the quota is not + // yet reached. + final NetworkPolicyManagerInternal npmi = LocalServices + .getService(NetworkPolicyManagerInternal.class); + npmi.onStatsProviderLimitReached("TEST"); + + // Verifies that the limit reached leads to a force update. + postMsgAndWaitForCompletion(); + verify(mStatsService).forceUpdate(); + } + + /** * Exhaustively test isUidNetworkingBlocked to output the expected results based on external * conditions. */ diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index aaf9799de777..8a3183f7abbd 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -18,7 +18,6 @@ package com.android.server.timedetector; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -26,7 +25,6 @@ import static org.junit.Assert.fail; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; -import android.content.Intent; import android.icu.util.Calendar; import android.icu.util.GregorianCalendar; import android.icu.util.TimeZone; @@ -78,8 +76,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); - mScript.verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, true /* expectNetworkBroadcast */) + mScript.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestPhoneSuggestion(phoneId, timeSuggestion); } @@ -118,8 +115,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime()); mScript.simulatePhoneTimeSuggestion(timeSuggestion1) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis1, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); } @@ -146,8 +142,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime()); mScript.simulatePhoneTimeSuggestion(timeSuggestion3) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis3, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis3) .assertLatestPhoneSuggestion(phoneId, timeSuggestion3); } } @@ -175,8 +170,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestPhoneSuggestion(phone1Id, null) .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion); } @@ -193,8 +187,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion); } @@ -227,8 +220,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion); } } @@ -265,8 +257,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1); mScript.simulatePhoneTimeSuggestion(timeSuggestion1) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis1, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); // The UTC time increment should be larger than the system clock update threshold so we @@ -304,8 +295,7 @@ public class TimeDetectorStrategyImplTest { PhoneTimeSuggestion timeSuggestion4 = createPhoneTimeSuggestion(phoneId, utcTime4); mScript.simulatePhoneTimeSuggestion(timeSuggestion4) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis4, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4) .assertLatestPhoneSuggestion(phoneId, timeSuggestion4); } @@ -339,8 +329,7 @@ public class TimeDetectorStrategyImplTest { // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis1, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); // Turn off auto time detection. @@ -367,8 +356,7 @@ public class TimeDetectorStrategyImplTest { // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis2, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2) .assertLatestPhoneSuggestion(phoneId, timeSuggestion2); } @@ -388,7 +376,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phoneSuggestion) .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, true /* expectedNetworkBroadcast */) + expectedSystemClockMillis /* expectedNetworkBroadcast */) .assertLatestPhoneSuggestion(phoneId, phoneSuggestion); // Look inside and check what the strategy considers the current best phone suggestion. @@ -416,8 +404,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); mScript.simulateManualTimeSuggestion(timeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, false /* expectNetworkBroadcast */); + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @Test @@ -439,8 +426,7 @@ public class TimeDetectorStrategyImplTest { long expectedAutoClockMillis = mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedAutoClockMillis, true /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); // Simulate the passage of time. @@ -463,8 +449,7 @@ public class TimeDetectorStrategyImplTest { long expectedManualClockMillis = mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime()); mScript.simulateManualTimeSuggestion(manualTimeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedManualClockMillis, false /* expectNetworkBroadcast */) + .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis) .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); // Simulate the passage of time. @@ -475,8 +460,7 @@ public class TimeDetectorStrategyImplTest { expectedAutoClockMillis = mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()); - mScript.verifySystemClockWasSetAndResetCallTracking( - expectedAutoClockMillis, true /* expectNetworkBroadcast */) + mScript.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); // Switch back to manual - nothing should happen to the clock. @@ -514,8 +498,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); mScript.simulateNetworkTimeSuggestion(timeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, false /* expectNetworkBroadcast */); + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @Test @@ -550,8 +533,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateNetworkTimeSuggestion(networkTimeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()), - false /* expectNetworkBroadcast */); + mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime())); // Check internal state. mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null) @@ -570,8 +552,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulatePhoneTimeSuggestion(phoneTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()), - true /* expectNetworkBroadcast */); + mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime())); // Check internal state. mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) @@ -622,8 +603,7 @@ public class TimeDetectorStrategyImplTest { // Verify the latest network time now wins. mScript.verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()), - false /* expectNetworkTimeBroadcast */); + mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime())); // Check internal state. mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) @@ -645,7 +625,6 @@ public class TimeDetectorStrategyImplTest { // Tracking operations. private boolean mSystemClockWasSet; - private Intent mBroadcastSent; @Override public int systemClockUpdateThresholdMillis() { @@ -689,12 +668,6 @@ public class TimeDetectorStrategyImplTest { mWakeLockAcquired = false; } - @Override - public void sendStickyBroadcast(Intent intent) { - assertNotNull(intent); - mBroadcastSent = intent; - } - // Methods below are for managing the fake's behavior. void pokeSystemClockUpdateThreshold(int thresholdMillis) { @@ -739,17 +712,8 @@ public class TimeDetectorStrategyImplTest { assertEquals(expectedSystemClockMillis, mSystemClockMillis); } - void verifyIntentWasBroadcast() { - assertTrue(mBroadcastSent != null); - } - - void verifyIntentWasNotBroadcast() { - assertNull(mBroadcastSent); - } - void resetCallTracking() { mSystemClockWasSet = false; - mBroadcastSent = null; } private void assertWakeLockAcquired() { @@ -832,17 +796,12 @@ public class TimeDetectorStrategyImplTest { Script verifySystemClockWasNotSetAndResetCallTracking() { mFakeCallback.verifySystemClockNotSet(); - mFakeCallback.verifyIntentWasNotBroadcast(); mFakeCallback.resetCallTracking(); return this; } - Script verifySystemClockWasSetAndResetCallTracking( - long expectedSystemClockMillis, boolean expectNetworkBroadcast) { + Script verifySystemClockWasSetAndResetCallTracking(long expectedSystemClockMillis) { mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis); - if (expectNetworkBroadcast) { - mFakeCallback.verifyIntentWasBroadcast(); - } mFakeCallback.resetCallTracking(); return this; } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java index f7cd6a3d2245..99065854445a 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java @@ -21,12 +21,10 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.hardware.soundtrigger.SoundTrigger; import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel; -import android.text.TextUtils; import android.util.Slog; -import java.util.Locale; +import java.io.PrintWriter; import java.util.UUID; /** @@ -39,7 +37,7 @@ public class SoundTriggerDbHelper extends SQLiteOpenHelper { static final boolean DBG = false; private static final String NAME = "st_sound_model.db"; - private static final int VERSION = 1; + private static final int VERSION = 2; // Sound trigger-based sound models. public static interface GenericSoundModelContract { @@ -47,15 +45,16 @@ public class SoundTriggerDbHelper extends SQLiteOpenHelper { public static final String KEY_MODEL_UUID = "model_uuid"; public static final String KEY_VENDOR_UUID = "vendor_uuid"; public static final String KEY_DATA = "data"; + public static final String KEY_MODEL_VERSION = "model_version"; } - // Table Create Statement for the sound trigger table private static final String CREATE_TABLE_ST_SOUND_MODEL = "CREATE TABLE " + GenericSoundModelContract.TABLE + "(" + GenericSoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY," + GenericSoundModelContract.KEY_VENDOR_UUID + " TEXT," - + GenericSoundModelContract.KEY_DATA + " BLOB" + " )"; + + GenericSoundModelContract.KEY_DATA + " BLOB," + + GenericSoundModelContract.KEY_MODEL_VERSION + " INTEGER" + " )"; public SoundTriggerDbHelper(Context context) { @@ -70,9 +69,13 @@ public class SoundTriggerDbHelper extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // TODO: For now, drop older tables and recreate new ones. - db.execSQL("DROP TABLE IF EXISTS " + GenericSoundModelContract.TABLE); - onCreate(db); + if (oldVersion == 1) { + // In version 2, a model version number was added. + Slog.d(TAG, "Adding model version column"); + db.execSQL("ALTER TABLE " + GenericSoundModelContract.TABLE + " ADD COLUMN " + + GenericSoundModelContract.KEY_MODEL_VERSION + " INTEGER DEFAULT -1"); + oldVersion++; + } } /** @@ -86,6 +89,7 @@ public class SoundTriggerDbHelper extends SQLiteOpenHelper { values.put(GenericSoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString()); values.put(GenericSoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString()); values.put(GenericSoundModelContract.KEY_DATA, soundModel.data); + values.put(GenericSoundModelContract.KEY_MODEL_VERSION, soundModel.version); try { return db.insertWithOnConflict(GenericSoundModelContract.TABLE, null, values, @@ -113,8 +117,10 @@ public class SoundTriggerDbHelper extends SQLiteOpenHelper { GenericSoundModelContract.KEY_DATA)); String vendor_uuid = c.getString( c.getColumnIndex(GenericSoundModelContract.KEY_VENDOR_UUID)); + int version = c.getInt( + c.getColumnIndex(GenericSoundModelContract.KEY_MODEL_VERSION)); return new GenericSoundModel(model_uuid, UUID.fromString(vendor_uuid), - data); + data, version); } while (c.moveToNext()); } } finally { @@ -142,4 +148,48 @@ public class SoundTriggerDbHelper extends SQLiteOpenHelper { } } } + + public void dump(PrintWriter pw) { + synchronized(this) { + String selectQuery = "SELECT * FROM " + GenericSoundModelContract.TABLE; + SQLiteDatabase db = getReadableDatabase(); + Cursor c = db.rawQuery(selectQuery, null); + try { + pw.println(" Enrolled GenericSoundModels:"); + if (c.moveToFirst()) { + String[] columnNames = c.getColumnNames(); + do { + for (String name : columnNames) { + int colNameIndex = c.getColumnIndex(name); + int type = c.getType(colNameIndex); + switch (type) { + case Cursor.FIELD_TYPE_STRING: + pw.printf(" %s: %s\n", name, + c.getString(colNameIndex)); + break; + case Cursor.FIELD_TYPE_BLOB: + pw.printf(" %s: data blob\n", name); + break; + case Cursor.FIELD_TYPE_INTEGER: + pw.printf(" %s: %d\n", name, + c.getInt(colNameIndex)); + break; + case Cursor.FIELD_TYPE_FLOAT: + pw.printf(" %s: %f\n", name, + c.getFloat(colNameIndex)); + break; + case Cursor.FIELD_TYPE_NULL: + pw.printf(" %s: null\n", name); + break; + } + } + pw.println(); + } while (c.moveToNext()); + } + } finally { + c.close(); + db.close(); + } + } + } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index e37755bddcaa..767010ae821a 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -1495,6 +1495,9 @@ public class SoundTriggerService extends SystemService { // log sEventLogger.dump(pw); + // enrolled models + mDbHelper.dump(pw); + // stats mSoundModelStatTracker.dump(pw); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java index dd7b5a8752e5..c58b6da64baa 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java @@ -27,6 +27,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.text.TextUtils; import android.util.Slog; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -43,7 +44,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { static final boolean DBG = false; private static final String NAME = "sound_model.db"; - private static final int VERSION = 6; + private static final int VERSION = 7; public static interface SoundModelContract { public static final String TABLE = "sound_model"; @@ -56,6 +57,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { public static final String KEY_LOCALE = "locale"; public static final String KEY_HINT_TEXT = "hint_text"; public static final String KEY_USERS = "users"; + public static final String KEY_MODEL_VERSION = "model_version"; } // Table Create Statement @@ -70,6 +72,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { + SoundModelContract.KEY_LOCALE + " TEXT," + SoundModelContract.KEY_HINT_TEXT + " TEXT," + SoundModelContract.KEY_USERS + " TEXT," + + SoundModelContract.KEY_MODEL_VERSION + " INTEGER," + "PRIMARY KEY (" + SoundModelContract.KEY_KEYPHRASE_ID + "," + SoundModelContract.KEY_LOCALE + "," + SoundModelContract.KEY_USERS + ")" @@ -138,6 +141,13 @@ public class DatabaseHelper extends SQLiteOpenHelper { } oldVersion++; } + if (oldVersion == 6) { + // In version 7, a model version number was added. + Slog.d(TAG, "Adding model version column"); + db.execSQL("ALTER TABLE " + SoundModelContract.TABLE + " ADD COLUMN " + + SoundModelContract.KEY_MODEL_VERSION + " INTEGER DEFAULT -1"); + oldVersion++; + } } /** @@ -155,6 +165,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { } values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE); values.put(SoundModelContract.KEY_DATA, soundModel.data); + values.put(SoundModelContract.KEY_MODEL_VERSION, soundModel.version); if (soundModel.keyphrases != null && soundModel.keyphrases.length == 1) { values.put(SoundModelContract.KEY_KEYPHRASE_ID, soundModel.keyphrases[0].id); @@ -250,6 +261,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { c.getColumnIndex(SoundModelContract.KEY_LOCALE)); String text = c.getString( c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT)); + int version = c.getInt( + c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION)); // Only add keyphrases meant for the current user. if (users == null) { @@ -282,7 +295,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { vendorUuid = UUID.fromString(vendorUuidString); } KeyphraseSoundModel model = new KeyphraseSoundModel( - UUID.fromString(modelUuid), vendorUuid, data, keyphrases); + UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version); if (DBG) { Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: " + model); @@ -325,6 +338,10 @@ public class DatabaseHelper extends SQLiteOpenHelper { return users; } + /** + * SoundModelRecord is no longer used, and it should only be used on database migration. + * This class does not need to be modified when modifying the database scheme. + */ private static class SoundModelRecord { public final String modelUuid; public final String vendorUuid; @@ -413,4 +430,48 @@ public class DatabaseHelper extends SQLiteOpenHelper { return a == b; } } + + public void dump(PrintWriter pw) { + synchronized(this) { + String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE; + SQLiteDatabase db = getReadableDatabase(); + Cursor c = db.rawQuery(selectQuery, null); + try { + pw.println(" Enrolled KeyphraseSoundModels:"); + if (c.moveToFirst()) { + String[] columnNames = c.getColumnNames(); + do { + for (String name : columnNames) { + int colNameIndex = c.getColumnIndex(name); + int type = c.getType(colNameIndex); + switch (type) { + case Cursor.FIELD_TYPE_STRING: + pw.printf(" %s: %s\n", name, + c.getString(colNameIndex)); + break; + case Cursor.FIELD_TYPE_BLOB: + pw.printf(" %s: data blob\n", name); + break; + case Cursor.FIELD_TYPE_INTEGER: + pw.printf(" %s: %d\n", name, + c.getInt(colNameIndex)); + break; + case Cursor.FIELD_TYPE_FLOAT: + pw.printf(" %s: %f\n", name, + c.getFloat(colNameIndex)); + break; + case Cursor.FIELD_TYPE_NULL: + pw.printf(" %s: null\n", name); + break; + } + } + pw.println(); + } while (c.moveToNext()); + } + } finally { + c.close(); + db.close(); + } + } + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 06c807421d1a..506c67e12528 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -159,9 +159,13 @@ public class VoiceInteractionManagerService extends SystemService { } } + private boolean isSupported(UserInfo user) { + return user.isFull(); + } + @Override - public boolean isSupported(UserInfo userInfo) { - return userInfo.isFull(); + public boolean isSupportedUser(TargetUser user) { + return isSupported(user.getUserInfo()); } @Override @@ -1343,6 +1347,7 @@ public class VoiceInteractionManagerService extends SystemService { pw.println(" mCurUserUnlocked: " + mCurUserUnlocked); pw.println(" mCurUserSupported: " + mCurUserSupported); dumpSupportedUsers(pw, " "); + mDbHelper.dump(pw); if (mImpl == null) { pw.println(" (No active implementation)"); return; diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING new file mode 100644 index 000000000000..d58566673eec --- /dev/null +++ b/telecomm/TEST_MAPPING @@ -0,0 +1,29 @@ +{ + "presubmit": [ + { + "name": "TeleServiceTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "TelecomUnitTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "TelephonyProviderTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +} + diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING new file mode 100644 index 000000000000..d58566673eec --- /dev/null +++ b/telephony/TEST_MAPPING @@ -0,0 +1,29 @@ +{ + "presubmit": [ + { + "name": "TeleServiceTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "TelecomUnitTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "TelephonyProviderTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +} + diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index a17a19c74e08..9bc534c2877a 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -27,10 +27,10 @@ import android.os.RemoteException; import android.os.UserHandle; import android.permission.IPermissionManager; import android.provider.Settings; -import android.util.Log; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -141,8 +141,8 @@ public final class CarrierAppUtils { ContentResolver contentResolver, int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed, ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) { - List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager, - userId, systemCarrierAppsDisabledUntilUsed); + List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper( + packageManager, userId, systemCarrierAppsDisabledUntilUsed); if (candidates == null || candidates.isEmpty()) { return; } @@ -178,15 +178,16 @@ public final class CarrierAppUtils { } } + int enabledSetting = packageManager.getApplicationEnabledSetting(packageName, + userId); if (hasPrivileges) { // Only update enabled state for the app on /system. Once it has been // updated we shouldn't touch it. - if (!ai.isUpdatedSystemApp() - && (ai.enabledSetting + if (enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT - || ai.enabledSetting + || enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED - || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) { + || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { Log.i(TAG, "Update state(" + packageName + "): ENABLED for user " + userId); packageManager.setSystemAppInstallState( @@ -204,9 +205,12 @@ public final class CarrierAppUtils { // Also enable any associated apps for this carrier app. if (associatedAppList != null) { for (ApplicationInfo associatedApp : associatedAppList) { - if (associatedApp.enabledSetting + int associatedAppEnabledSetting = + packageManager.getApplicationEnabledSetting( + associatedApp.packageName, userId); + if (associatedAppEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT - || associatedApp.enabledSetting + || associatedAppEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED || (associatedApp.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { @@ -231,8 +235,7 @@ public final class CarrierAppUtils { } else { // No carrier privileges // Only update enabled state for the app on /system. Once it has been // updated we shouldn't touch it. - if (!ai.isUpdatedSystemApp() - && ai.enabledSetting + if (enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { Log.i(TAG, "Update state(" + packageName @@ -249,7 +252,10 @@ public final class CarrierAppUtils { if (!hasRunOnce) { if (associatedAppList != null) { for (ApplicationInfo associatedApp : associatedAppList) { - if (associatedApp.enabledSetting + int associatedAppEnabledSetting = + packageManager.getApplicationEnabledSetting( + associatedApp.packageName, userId); + if (associatedAppEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && (associatedApp.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { @@ -360,6 +366,31 @@ public final class CarrierAppUtils { return apps; } + private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper( + IPackageManager packageManager, + int userId, + ArraySet<String> systemCarrierAppsDisabledUntilUsed) { + if (systemCarrierAppsDisabledUntilUsed == null) { + return null; + } + + int size = systemCarrierAppsDisabledUntilUsed.size(); + if (size == 0) { + return null; + } + + List<ApplicationInfo> apps = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i); + ApplicationInfo ai = + getApplicationInfoIfNotUpdatedSystemApp(packageManager, userId, packageName); + if (ai != null) { + apps.add(ai); + } + } + return apps; + } + private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper( IPackageManager packageManager, int userId, @@ -372,11 +403,11 @@ public final class CarrierAppUtils { systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i); for (int j = 0; j < associatedAppPackages.size(); j++) { ApplicationInfo ai = - getApplicationInfoIfSystemApp( + getApplicationInfoIfNotUpdatedSystemApp( packageManager, userId, associatedAppPackages.get(j)); // Only update enabled state for the app on /system. Once it has been updated we // shouldn't touch it. - if (ai != null && !ai.isUpdatedSystemApp()) { + if (ai != null) { List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage); if (appList == null) { appList = new ArrayList<>(); @@ -390,6 +421,26 @@ public final class CarrierAppUtils { } @Nullable + private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp( + IPackageManager packageManager, + int userId, + String packageName) { + try { + ApplicationInfo ai = packageManager.getApplicationInfo(packageName, + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS + | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS + | PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_FACTORY_ONLY, userId); + if (ai != null) { + return ai; + } + } catch (RemoteException e) { + Log.w(TAG, "Could not reach PackageManager", e); + } + return null; + } + + @Nullable private static ApplicationInfo getApplicationInfoIfSystemApp( IPackageManager packageManager, int userId, @@ -397,8 +448,9 @@ public final class CarrierAppUtils { try { ApplicationInfo ai = packageManager.getApplicationInfo(packageName, PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS - | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, userId); - if (ai != null && ai.isSystemApp()) { + | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS + | PackageManager.MATCH_SYSTEM_ONLY, userId); + if (ai != null) { return ai; } } catch (RemoteException e) { diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java index e01deb2802ff..1e1cdba70ad0 100644 --- a/telephony/java/android/telephony/CallQuality.java +++ b/telephony/java/android/telephony/CallQuality.java @@ -80,6 +80,9 @@ public final class CallQuality implements Parcelable { private int mMaxRelativeJitter; private int mAverageRoundTripTime; private int mCodecType; + private boolean mRtpInactivityDetected; + private boolean mRxSilenceDetected; + private boolean mTxSilenceDetected; /** @hide **/ public CallQuality(Parcel in) { @@ -94,6 +97,9 @@ public final class CallQuality implements Parcelable { mMaxRelativeJitter = in.readInt(); mAverageRoundTripTime = in.readInt(); mCodecType = in.readInt(); + mRtpInactivityDetected = in.readBoolean(); + mRxSilenceDetected = in.readBoolean(); + mTxSilenceDetected = in.readBoolean(); } /** @hide **/ @@ -109,7 +115,7 @@ public final class CallQuality implements Parcelable { * @param numRtpPacketsReceived RTP packets received from network * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never * transmitted - * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved + * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received * @param averageRelativeJitter average relative jitter in milliseconds * @param maxRelativeJitter maximum relative jitter in milliseconds * @param averageRoundTripTime average round trip delay in milliseconds @@ -127,6 +133,48 @@ public final class CallQuality implements Parcelable { int maxRelativeJitter, int averageRoundTripTime, int codecType) { + this(downlinkCallQualityLevel, uplinkCallQualityLevel, callDuration, + numRtpPacketsTransmitted, numRtpPacketsReceived, numRtpPacketsTransmittedLost, + numRtpPacketsNotReceived, averageRelativeJitter, maxRelativeJitter, + averageRoundTripTime, codecType, false, false, false); + } + + /** + * Constructor. + * + * @param callQualityLevel the call quality level (see #CallQualityLevel) + * @param callDuration the call duration in milliseconds + * @param numRtpPacketsTransmitted RTP packets sent to network + * @param numRtpPacketsReceived RTP packets received from network + * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never + * transmitted + * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received + * @param averageRelativeJitter average relative jitter in milliseconds + * @param maxRelativeJitter maximum relative jitter in milliseconds + * @param averageRoundTripTime average round trip delay in milliseconds + * @param codecType the codec type + * @param rtpInactivityDetected True if no incoming RTP is received for a continuous duration of + * 4 seconds + * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds + * immediately after call is connected + * @param txSilenceDetected True if only silence RTP packets are sent for 20 seconds immediately + * after call is connected + */ + public CallQuality( + @CallQualityLevel int downlinkCallQualityLevel, + @CallQualityLevel int uplinkCallQualityLevel, + int callDuration, + int numRtpPacketsTransmitted, + int numRtpPacketsReceived, + int numRtpPacketsTransmittedLost, + int numRtpPacketsNotReceived, + int averageRelativeJitter, + int maxRelativeJitter, + int averageRoundTripTime, + int codecType, + boolean rtpInactivityDetected, + boolean rxSilenceDetected, + boolean txSilenceDetected) { this.mDownlinkCallQualityLevel = downlinkCallQualityLevel; this.mUplinkCallQualityLevel = uplinkCallQualityLevel; this.mCallDuration = callDuration; @@ -138,6 +186,9 @@ public final class CallQuality implements Parcelable { this.mMaxRelativeJitter = maxRelativeJitter; this.mAverageRoundTripTime = averageRoundTripTime; this.mCodecType = codecType; + this.mRtpInactivityDetected = rtpInactivityDetected; + this.mRxSilenceDetected = rxSilenceDetected; + this.mTxSilenceDetected = txSilenceDetected; } // getters @@ -226,6 +277,29 @@ public final class CallQuality implements Parcelable { } /** + * Returns true if no rtp packets are received continuously for the last 4 seconds + */ + public boolean isRtpInactivityDetected() { + return mRtpInactivityDetected; + } + + /** + * Returns true if only silence rtp packets are received for a duration of 20 seconds starting + * at call setup + */ + public boolean isIncomingSilenceDetected() { + return mRxSilenceDetected; + } + + /** + * Returns true if only silence rtp packets are sent for a duration of 20 seconds starting at + * call setup + */ + public boolean isOutgoingSilenceDetected() { + return mTxSilenceDetected; + } + + /** * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in * {@link ImsStreamMediaProfile}. * @@ -270,6 +344,9 @@ public final class CallQuality implements Parcelable { + " maxRelativeJitter=" + mMaxRelativeJitter + " averageRoundTripTime=" + mAverageRoundTripTime + " codecType=" + mCodecType + + " rtpInactivityDetected=" + mRtpInactivityDetected + + " txSilenceDetected=" + mRxSilenceDetected + + " rxSilenceDetected=" + mTxSilenceDetected + "}"; } @@ -286,7 +363,10 @@ public final class CallQuality implements Parcelable { mAverageRelativeJitter, mMaxRelativeJitter, mAverageRoundTripTime, - mCodecType); + mCodecType, + mRtpInactivityDetected, + mRxSilenceDetected, + mTxSilenceDetected); } @Override @@ -311,7 +391,10 @@ public final class CallQuality implements Parcelable { && mAverageRelativeJitter == s.mAverageRelativeJitter && mMaxRelativeJitter == s.mMaxRelativeJitter && mAverageRoundTripTime == s.mAverageRoundTripTime - && mCodecType == s.mCodecType); + && mCodecType == s.mCodecType + && mRtpInactivityDetected == s.mRtpInactivityDetected + && mRxSilenceDetected == s.mRxSilenceDetected + && mTxSilenceDetected == s.mTxSilenceDetected); } /** @@ -336,6 +419,9 @@ public final class CallQuality implements Parcelable { dest.writeInt(mMaxRelativeJitter); dest.writeInt(mAverageRoundTripTime); dest.writeInt(mCodecType); + dest.writeBoolean(mRtpInactivityDetected); + dest.writeBoolean(mRxSilenceDetected); + dest.writeBoolean(mTxSilenceDetected); } public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 6a622378dac7..86178334879f 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -672,6 +672,12 @@ public class CarrierConfigManager { "carrier_promote_wfc_on_call_fail_bool"; /** + * Flag specifying whether provisioning is required for RCS. + */ + public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = + "carrier_rcs_provisioning_required_bool"; + + /** * Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi * Calling. */ @@ -848,9 +854,12 @@ public class CarrierConfigManager { "carrier_force_disable_etws_cmas_test_bool"; /** - * The default flag specifying whether "Turn on Notifications" option will be always shown in - * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not. + * The default flag specifying whether "Allow alerts" option will be always shown in + * emergency alerts settings regardless developer options is turned on or not. + * + * @deprecated The allow alerts option is always shown now. No longer need a config for that. */ + @Deprecated public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; @@ -1627,8 +1636,8 @@ public class CarrierConfigManager { * Defines carrier-specific actions which act upon * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED * and configured signal args: - * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType}, - * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode} + * {@link TelephonyManager#EXTRA_APN_TYPE apnType}, + * {@link TelephonyManager#EXTRA_ERROR_CODE errorCode} * used for customization of the default carrier app * Format: * { @@ -3497,6 +3506,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2); sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2); sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false); + sDefaults.putBoolean(KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, false); diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index eb328a705e56..da0ac39fe1d3 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -744,11 +744,7 @@ public final class SmsManager { "Invalid pdu format. format must be either 3gpp or 3gpp2"); } try { - ISms iSms = ISms.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSmsServiceRegisterer() - .get()); + ISms iSms = TelephonyManager.getSmsService(); if (iSms != null) { iSms.injectSmsPduForSubscriber( getSubscriptionId(), pdu, format, receivedIntent); @@ -1571,7 +1567,7 @@ public final class SmsManager { * the service does not exist. */ private static ISms getISmsServiceOrThrow() { - ISms iSms = getISmsService(); + ISms iSms = TelephonyManager.getSmsService(); if (iSms == null) { throw new UnsupportedOperationException("Sms is not supported"); } @@ -1579,11 +1575,7 @@ public final class SmsManager { } private static ISms getISmsService() { - return ISms.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSmsServiceRegisterer() - .get()); + return TelephonyManager.getSmsService(); } /** @@ -2017,11 +2009,7 @@ public final class SmsManager { public boolean isSMSPromptEnabled() { ISms iSms = null; try { - iSms = ISms.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSmsServiceRegisterer() - .get()); + iSms = TelephonyManager.getSmsService(); return iSms.isSMSPromptEnabled(); } catch (RemoteException ex) { return false; @@ -2448,14 +2436,18 @@ public final class SmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if contentUri is empty + * @deprecated use {@link MmsManager#sendMultimediaMessage} instead. */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } - MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri, - locationUrl, configOverrides, sentIntent); + MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); + if (m != null) { + m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides, + sentIntent); + } } /** @@ -2479,6 +2471,7 @@ public final class SmsManager { * @param downloadedIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty + * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead. */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { @@ -2488,8 +2481,11 @@ public final class SmsManager { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } - MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl, - contentUri, configOverrides, downloadedIntent); + MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); + if (m != null) { + m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri, + configOverrides, downloadedIntent); + } } // MMS send/download failure result codes @@ -2531,9 +2527,9 @@ public final class SmsManager { * </p> * * @return the bundle key/values pairs that contains MMS configuration values + * or an empty Bundle if they cannot be found. */ - @Nullable - public Bundle getCarrierConfigValues() { + @NonNull public Bundle getCarrierConfigValues() { try { ISms iSms = getISmsService(); if (iSms != null) { @@ -2542,7 +2538,7 @@ public final class SmsManager { } catch (RemoteException ex) { // ignore it } - return null; + return new Bundle(); } /** diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index c217b8b83c26..40a7619a3ee3 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -20,8 +20,13 @@ import com.android.telephony.Rlog; import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.os.Binder; @@ -31,8 +36,10 @@ import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; import com.android.internal.telephony.Sms7BitEncodingTranslator; import com.android.internal.telephony.SmsConstants; +import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; +import com.android.internal.telephony.cdma.sms.UserData; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -56,6 +63,16 @@ public class SmsMessage { UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3; } + /** @hide */ + @IntDef(prefix = { "ENCODING_" }, value = { + ENCODING_UNKNOWN, + ENCODING_7BIT, + ENCODING_8BIT, + ENCODING_16BIT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EncodingSize {} + /** User data text encoding code unit size */ public static final int ENCODING_UNKNOWN = 0; public static final int ENCODING_7BIT = 1; @@ -633,6 +650,83 @@ public class SmsMessage { } /** + * Get an SMS-SUBMIT PDU's encoded message. + * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages. + * + * @param isTypeGsm true when message's type is GSM, false when type is CDMA + * @param destinationAddress the address of the destination for the message + * @param message message content + * @param encoding User data text encoding code unit size + * @param languageTable GSM national language table to use, specified by 3GPP + * 23.040 9.2.3.24.16 + * @param languageShiftTable GSM national language shift table to use, specified by 3GPP + * 23.040 9.2.3.24.15 + * @param refNumber parameter to create SmsHeader + * @param seqNumber parameter to create SmsHeader + * @param msgCount parameter to create SmsHeader + * @return a byte[] containing the encoded message + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SystemApi + @NonNull + public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm, + @NonNull String destinationAddress, + @NonNull String message, + @EncodingSize int encoding, int languageTable, + int languageShiftTable, int refNumber, + int seqNumber, int msgCount) { + byte[] data; + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = refNumber; + concatRef.seqNumber = seqNumber; // 1-based sequence + concatRef.msgCount = msgCount; + // We currently set this to true since our messaging app will never + // send more than 255 parts (it converts the message to MMS well before that). + // However, we should support 3rd party messaging apps that might need 16-bit + // references + // Note: It's not sufficient to just flip this bit to true; it will have + // ripple effects (several calculations assume 8-bit ref). + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + + /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding + * will be determined(again) by getSubmitPdu(). + * All packets need to be encoded using the same encoding, as the bMessage + * only have one filed to describe the encoding for all messages in a concatenated + * SMS... */ + if (encoding == ENCODING_7BIT) { + smsHeader.languageTable = languageTable; + smsHeader.languageShiftTable = languageShiftTable; + } + + if (isTypeGsm) { + data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null, + destinationAddress, message, false, + SmsHeader.toByteArray(smsHeader), encoding, languageTable, + languageShiftTable).encodedMessage; + } else { // SMS_TYPE_CDMA + UserData uData = new UserData(); + uData.payloadStr = message; + uData.userDataHeader = smsHeader; + if (encoding == ENCODING_7BIT) { + uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; + } else { // assume UTF-16 + uData.msgEncoding = UserData.ENCODING_UNICODE_16; + } + uData.msgEncodingSet = true; + data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( + destinationAddress, uData, false).encodedMessage; + } + if (data == null) { + return new byte[0]; + } + return data; + } + + /** * Returns the address of the SMS service center that relayed this message * or null if there is none. */ diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 4510fede4e8e..cb4462f88581 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1146,11 +1146,7 @@ public class SubscriptionManager { SubscriptionInfo subInfo = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1184,11 +1180,7 @@ public class SubscriptionManager { SubscriptionInfo result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1222,11 +1214,7 @@ public class SubscriptionManager { SubscriptionInfo result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1250,11 +1238,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1335,11 +1319,7 @@ public class SubscriptionManager { List<SubscriptionInfo> activeList = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1390,11 +1370,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1433,11 +1409,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName()); } @@ -1466,11 +1438,7 @@ public class SubscriptionManager { public void requestEmbeddedSubscriptionInfoListRefresh() { int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } @@ -1499,11 +1467,7 @@ public class SubscriptionManager { @SystemApi public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } @@ -1524,11 +1488,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoCount(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1557,11 +1517,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1582,11 +1538,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCountMax(); } @@ -1643,11 +1595,7 @@ public class SubscriptionManager { } try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub == null) { Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null"); return; @@ -1681,11 +1629,7 @@ public class SubscriptionManager { } try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub == null) { Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null"); return; @@ -1788,11 +1732,7 @@ public class SubscriptionManager { int result = INVALID_SIM_SLOT_INDEX; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getSlotIndex(subscriptionId); } @@ -1826,11 +1766,7 @@ public class SubscriptionManager { int[] subId = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getSubId(slotIndex); } @@ -1854,11 +1790,7 @@ public class SubscriptionManager { int result = INVALID_PHONE_INDEX; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getPhoneId(subId); } @@ -1892,11 +1824,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultSubId(); } @@ -1919,11 +1847,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultVoiceSubId(); } @@ -1953,11 +1877,7 @@ public class SubscriptionManager { public void setDefaultVoiceSubscriptionId(int subscriptionId) { if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setDefaultVoiceSubId(subscriptionId); } @@ -2005,11 +1925,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultSmsSubId(); } @@ -2035,11 +1951,7 @@ public class SubscriptionManager { public void setDefaultSmsSubId(int subscriptionId) { if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setDefaultSmsSubId(subscriptionId); } @@ -2077,11 +1989,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultDataSubId(); } @@ -2107,11 +2015,7 @@ public class SubscriptionManager { public void setDefaultDataSubId(int subscriptionId) { if (VDBG) logd("setDataSubscription sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setDefaultDataSubId(subscriptionId); } @@ -2142,11 +2046,7 @@ public class SubscriptionManager { /** @hide */ public void clearSubscriptionInfo() { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.clearSubInfo(); } @@ -2282,11 +2182,7 @@ public class SubscriptionManager { */ public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { int[] subId = iSub.getActiveSubIdList(visibleOnly); if (subId != null) return subId; @@ -2337,11 +2233,7 @@ public class SubscriptionManager { int simState = TelephonyManager.SIM_STATE_UNKNOWN; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { simState = iSub.getSimStateForSlotIndex(slotIndex); } @@ -2360,11 +2252,7 @@ public class SubscriptionManager { */ public static void setSubscriptionProperty(int subId, String propKey, String propValue) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setSubscriptionProperty(subId, propKey, propValue); } @@ -2384,11 +2272,7 @@ public class SubscriptionManager { Context context) { String resultValue = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { resultValue = iSub.getSubscriptionProperty(subId, propKey, context.getOpPackageName(), context.getFeatureId()); @@ -2530,11 +2414,7 @@ public class SubscriptionManager { @UnsupportedAppUsage public boolean isActiveSubId(int subId) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.isActiveSubId(subId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -2837,11 +2717,7 @@ public class SubscriptionManager { @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) { if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub == null) return; ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { @@ -2884,11 +2760,7 @@ public class SubscriptionManager { public int getPreferredDataSubscriptionId() { int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { preferredSubId = iSub.getPreferredDataSubscriptionId(); } @@ -2919,11 +2791,7 @@ public class SubscriptionManager { List<SubscriptionInfo> subInfoList = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature); } @@ -3024,11 +2892,7 @@ public class SubscriptionManager { ParcelUuid groupUuid = null; int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug); } else { @@ -3078,11 +2942,7 @@ public class SubscriptionManager { int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug); } else { @@ -3134,11 +2994,7 @@ public class SubscriptionManager { int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug); } else { @@ -3183,11 +3039,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature); } else { @@ -3300,11 +3152,7 @@ public class SubscriptionManager { logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable); } try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.setSubscriptionEnabled(enable, subscriptionId); } @@ -3393,11 +3241,7 @@ public class SubscriptionManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int subscriptionId) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.isSubscriptionEnabled(subscriptionId); } @@ -3420,11 +3264,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getEnabledSubscriptionId(slotIndex); } @@ -3450,11 +3290,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = helper.callMethod(iSub); } @@ -3477,11 +3313,7 @@ public class SubscriptionManager { */ public static int getActiveDataSubscriptionId() { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.getActiveDataSubscriptionId(); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f3214274e396..00d9f3ca1728 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -57,6 +57,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; @@ -94,12 +95,15 @@ import android.util.Log; import android.util.Pair; import com.android.ims.internal.IImsServiceFeatureCallback; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.IOns; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ISetOpportunisticDataCallback; +import com.android.internal.telephony.ISms; +import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.IUpdateAvailableNetworksCallback; @@ -301,6 +305,21 @@ public class TelephonyManager { private SubscriptionManager mSubscriptionManager; private TelephonyScanManager mTelephonyScanManager; + /** Cached service handles, cleared by resetServiceHandles() at death */ + private static final Object sCacheLock = new Object(); + + /** @hide */ + private static boolean sServiceHandleCacheEnabled = true; + + @GuardedBy("sCacheLock") + private static IPhoneSubInfo sIPhoneSubInfo; + @GuardedBy("sCacheLock") + private static ISub sISub; + @GuardedBy("sCacheLock") + private static ISms sISms; + @GuardedBy("sCacheLock") + private static final DeathRecipient sServiceDeath = new DeathRecipient(); + /** Enum indicating multisim variants * DSDS - Dual SIM Dual Standby * DSDA - Dual SIM Dual Active @@ -1444,24 +1463,6 @@ public class TelephonyManager { "android.telephony.extra.SIM_COMBINATION_NAMES"; /** - * Broadcast Action: The time was set by the carrier (typically by the NITZ string). - * This is a sticky broadcast. - * The intent will have the following extra values:</p> - * <ul> - * <li><em>time</em> - The time as a long in UTC milliseconds.</li> - * </ul> - * - * <p class="note"> - * Requires the READ_PHONE_STATE permission. - * - * <p class="note">This is a protected intent that can only be sent by the system. - * - * @hide - */ - @SystemApi - public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME"; - - /** * <p>Broadcast Action: The emergency callback mode is changed. * <ul> * <li><em>phoneinECMState</em> - A boolean value,true=phone in ECM, false=ECM off</li> @@ -1482,6 +1483,193 @@ public class TelephonyManager { = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED"; /** + * <p>Broadcast Action: when data connections get redirected with validation failure. + * intended for sim/account status checks and only sent to the specified carrier app + * The intent will have the following extra values:</p> + * <ul> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> + * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> + * <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>redirection url string</dd> + * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * </ul> + * <p class="note">This is a protected intent that can only be sent by the system.</p> + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = + "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; + + /** + * <p>Broadcast Action: when data connections setup fails. + * intended for sim/account status checks and only sent to the specified carrier app + * The intent will have the following extra values:</p> + * <ul> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> + * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> + * <li>{@link #EXTRA_ERROR_CODE}</li><dd>A integer with dataFailCause.</dd> + * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * </ul> + * <p class="note">This is a protected intent that can only be sent by the system. </p> + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = + "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + + /** + * <p>Broadcast Action: when pco value is available. + * intended for sim/account status checks and only sent to the specified carrier app + * The intent will have the following extra values:</p> + * <ul> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> + * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> + * <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>A string with the protocol of the apn connection + * (IP,IPV6, IPV4V6)</dd> + * <li>{@link #EXTRA_APN_PROTOCOL_INT}</li><dd>A integer with the protocol of the apn + * connection (IP,IPV6, IPV4V6)</dd> + * <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the pco id for the data.</dd> + * <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of pco data read from modem.</dd> + * <li>subId</li><dd>Sub Id which associated the data connection.</dd> + * </ul> + * <p class="note">This is a protected intent that can only be sent by the system. </p> + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = + "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; + + /** + * <p>Broadcast Action: when system default network available/unavailable with + * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when + * other network becomes system default network, Wi-Fi for example. + * The intent will have the following extra values:</p> + * <ul> + * <li>{@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}</li> + * <dd>A boolean indicates default network available.</dd> + * <li>subId</li><dd>Sub Id which associated the default data.</dd> + * </ul> + * <p class="note">This is a protected intent that can only be sent by the system. </p> + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = + "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + + /** + * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent. + * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app + * The intent will have the following extra values:</p> + * <ul> + * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * </ul> + * <p class="note">This is a protected intent that can only be sent by the system.</p> + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_CARRIER_SIGNAL_RESET = + "com.android.internal.telephony.CARRIER_SIGNAL_RESET"; + + // CARRIER_SIGNAL_ACTION extra keys + /** + * An string extra of redirected url upon {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_REDIRECTION_URL = "redirectionUrl"; + + /** + * An integer extra of error code upon {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. + * Check {@link DataFailCause} for all possible values. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_ERROR_CODE = "errorCode"; + + /** + * An string extra of corresponding apn type upon + * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and + * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. + * @deprecated This is kept for backward compatibility reason. Use {@link #EXTRA_APN_TYPE_INT} + * instead. + * + * @hide + */ + @SystemApi + @Deprecated + @SuppressLint("ActionValue") + public static final String EXTRA_APN_TYPE = "apnType"; + + /** + * An string integer of corresponding apn type upon + * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and + * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. + * Check {@link ApnSetting} TYPE_* for its values. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_APN_TYPE_INT = "apnTypeInt"; + + /** + * An string extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon + * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. + * @deprecated This is kept for backward compatibility reason. + * Use {@link #EXTRA_APN_PROTOCOL_INT} instead. + * + * @hide + */ + @SystemApi + @Deprecated + @SuppressLint("ActionValue") + public static final String EXTRA_APN_PROTOCOL = "apnProto"; + + /** + * An integer extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon + * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. + * Check {@link ApnSetting} PROTOCOL_* for its values. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt"; + + /** + * An integer extra indicating the pco id for the data upon + * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_PCO_ID = "pcoId"; + + /** + * An extra of byte array of pco data read from modem upon + * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_PCO_VALUE = "pcoValue"; + + /** + * An boolean extra indicating default network available upon + * {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcasts. + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable"; + + /** * <p>Broadcast Action: The emergency call state is changed. * <ul> * <li><em>phoneInEmergencyCall</em> - A boolean value, true if phone in emergency call, @@ -1680,7 +1868,7 @@ public class TelephonyManager { public String getDeviceId(int slotIndex) { // FIXME this assumes phoneId == slotIndex try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(), @@ -1934,7 +2122,7 @@ public class TelephonyManager { private String getNaiBySubscriberId(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(), @@ -3623,7 +3811,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getSimSerialNumber(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(), @@ -3897,7 +4085,7 @@ public class TelephonyManager { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public String getSubscriberId(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(), @@ -3933,7 +4121,7 @@ public class TelephonyManager { @Nullable public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) { Rlog.e(TAG,"IMSI error: Subscriber Info is null"); return null; @@ -3976,7 +4164,7 @@ public class TelephonyManager { @SystemApi public void resetCarrierKeysForImsiEncryption() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) { Rlog.e(TAG, "IMSI error: Subscriber Info is null"); if (!isSystemProcess()) { @@ -4041,7 +4229,7 @@ public class TelephonyManager { */ public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return; info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(), imsiEncryptionInfo); @@ -4065,7 +4253,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getGroupIdLevel1() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(), @@ -4089,7 +4277,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getGroupIdLevel1(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(), @@ -4152,7 +4340,7 @@ public class TelephonyManager { return number; } try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(), @@ -4243,7 +4431,7 @@ public class TelephonyManager { return alphaTag; } try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(), @@ -4331,7 +4519,7 @@ public class TelephonyManager { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public String getMsisdn(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId()); @@ -4365,7 +4553,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getVoiceMailNumber(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(), @@ -4964,7 +5152,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getVoiceMailAlphaTag(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(), @@ -5012,7 +5200,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getIsimImpi() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Impi based on subId @@ -5039,7 +5227,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Domain based on subId @@ -5063,7 +5251,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Impu based on subId @@ -5076,19 +5264,6 @@ public class TelephonyManager { } } - /** - * @hide - */ - @UnsupportedAppUsage - private IPhoneSubInfo getSubscriberInfo() { - // get it each time because that process crashes a lot - return IPhoneSubInfo.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getPhoneSubServiceRegisterer() - .get()); - } - /** * Device call state: No activity. */ @@ -6825,7 +7000,7 @@ public class TelephonyManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Ist based on subId @@ -6847,7 +7022,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String[] getIsimPcscf() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Pcscf based on subId @@ -6928,7 +7103,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getIccAuthentication(int subId, int appType, int authType, String data) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getIccSimChallengeResponse(subId, appType, authType, data); @@ -12204,4 +12379,150 @@ public class TelephonyManager { } return false; } + + private static class DeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + resetServiceCache(); + } + } + + /** + * Reset everything in the service cache; if one handle died then they are + * all probably broken. + * @hide + */ + private static void resetServiceCache() { + synchronized (sCacheLock) { + if (sISub != null) { + sISub.asBinder().unlinkToDeath(sServiceDeath, 0); + sISub = null; + } + if (sISms != null) { + sISms.asBinder().unlinkToDeath(sServiceDeath, 0); + sISms = null; + } + if (sIPhoneSubInfo != null) { + sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0); + sIPhoneSubInfo = null; + } + } + } + + /** + * @hide + */ + static IPhoneSubInfo getSubscriberInfoService() { + if (!sServiceHandleCacheEnabled) { + return IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); + } + + if (sIPhoneSubInfo == null) { + IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sIPhoneSubInfo == null && temp != null) { + try { + sIPhoneSubInfo = temp; + sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sIPhoneSubInfo = null; + } + } + } + } + return sIPhoneSubInfo; + } + + /** + * @hide + */ + static ISub getSubscriptionService() { + if (!sServiceHandleCacheEnabled) { + return ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); + } + + if (sISub == null) { + ISub temp = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sISub == null && temp != null) { + try { + sISub = temp; + sISub.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sISub = null; + } + } + } + } + return sISub; + } + + /** + * @hide + */ + static ISms getSmsService() { + if (!sServiceHandleCacheEnabled) { + return ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); + } + + if (sISms == null) { + ISms temp = ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sISms == null && temp != null) { + try { + sISms = temp; + sISms.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sISms = null; + } + } + } + } + return sISms; + } + + /** + * Disables service handle caching for tests that utilize mock services. + * @hide + */ + @VisibleForTesting + public static void disableServiceHandleCaching() { + sServiceHandleCacheEnabled = false; + } + + /** + * Reenables service handle caching. + * @hide + */ + @VisibleForTesting + public static void enableServiceHandleCaching() { + sServiceHandleCacheEnabled = true; + } } diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 7ffba7abffd6..6005f77605a6 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -34,6 +34,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsConfigCallback; import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -546,6 +547,54 @@ public class ProvisioningManager { } /** + * Get the provisioning status for the IMS RCS capability specified. + * + * If provisioning is not required for the queried + * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return + * {@code true}. + * + * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL + * @return true if the device is provisioned for the capability or does not require + * provisioning, false if the capability does require provisioning and has not been + * provisioned yet. + */ + @WorkerThread + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean getRcsProvisioningStatusForCapability( + @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + try { + return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Set the provisioning status for the IMS RCS capability using the specified subscription. + * + * Provisioning may or may not be required, depending on the carrier configuration. If + * provisioning is not required for the carrier associated with this subscription or the device + * does not support the capability/technology combination specified, this operation will be a + * no-op. + * + * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL + * @param isProvisioned true if the device is provisioned for the RCS capability specified, + * false otherwise. + */ + @WorkerThread + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setRcsProvisioningStatusForCapability( + @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + boolean isProvisioned) { + try { + getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability, + isProvisioned); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Notify the framework that an RCS autoconfiguration XML file has been received for * provisioning. * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index b846a1029c68..28f3974162b7 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1977,6 +1977,17 @@ interface ITelephony { */ boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech); + /** + * Get the provisioning status for the IMS Rcs capability specified. + */ + boolean getRcsProvisioningStatusForCapability(int subId, int capability); + + /** + * Set the provisioning status for the IMS Rcs capability using the specified subscription. + */ + void setRcsProvisioningStatusForCapability(int subId, int capability, + boolean isProvisioned); + /** Is the capability and tech flagged as provisioned in the cache */ boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech); diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 1fbe8cb49836..a15f73cf348d 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -328,85 +328,6 @@ public class TelephonyIntents { "android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED"; /** - * <p>Broadcast Action: when data connections get redirected with validation failure. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> - * <ul> - * <li>apnType</li><dd>A string with the apn type.</dd> - * <li>redirectionUrl</li><dd>redirection url string</dd> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> - * </ul> - * <p class="note">This is a protected intent that can only be sent by the system.</p> - */ - public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = - "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; - /** - * <p>Broadcast Action: when data connections setup fails. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> - * <ul> - * <li>apnType</li><dd>A string with the apn type.</dd> - * <li>errorCode</li><dd>A integer with dataFailCause.</dd> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> - * </ul> - * <p class="note">This is a protected intent that can only be sent by the system. </p> - */ - public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = - "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; - - /** - * <p>Broadcast Action: when pco value is available. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> - * <ul> - * <li>apnType</li><dd>A string with the apn type.</dd> - * <li>apnProto</li><dd>A string with the protocol of the apn connection (IP,IPV6, - * IPV4V6)</dd> - * <li>pcoId</li><dd>An integer indicating the pco id for the data.</dd> - * <li>pcoValue</li><dd>A byte array of pco data read from modem.</dd> - * <li>subId</li><dd>Sub Id which associated the data connection.</dd> - * </ul> - * <p class="note">This is a protected intent that can only be sent by the system. </p> - */ - public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = - "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; - - /** - * <p>Broadcast Action: when system default network available/unavailable with - * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when - * other network becomes system default network, Wi-Fi for example. - * The intent will have the following extra values:</p> - * <ul> - * <li>defaultNetworkAvailable</li><dd>A boolean indicates default network available.</dd> - * <li>subId</li><dd>Sub Id which associated the default data.</dd> - * </ul> - * <p class="note">This is a protected intent that can only be sent by the system. </p> - */ - public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = - "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; - - /** - * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent. - * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app - * The intent will have the following extra values:</p> - * <ul> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> - * </ul> - * <p class="note">This is a protected intent that can only be sent by the system.</p> - */ - public static final String ACTION_CARRIER_SIGNAL_RESET = - "com.android.internal.telephony.CARRIER_SIGNAL_RESET"; - - // CARRIER_SIGNAL_ACTION extra keys - public static final String EXTRA_REDIRECTION_URL_KEY = "redirectionUrl"; - public static final String EXTRA_ERROR_CODE_KEY = "errorCode"; - public static final String EXTRA_APN_TYPE_KEY = "apnType"; - public static final String EXTRA_APN_PROTO_KEY = "apnProto"; - public static final String EXTRA_PCO_ID_KEY = "pcoId"; - public static final String EXTRA_PCO_VALUE_KEY = "pcoValue"; - public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY = "defaultNetworkAvailable"; - - /** * Broadcast action to trigger CI OMA-DM Session. */ public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java index eed7159ffddc..ca4ba63142a2 100644 --- a/tests/net/common/java/android/net/CaptivePortalTest.java +++ b/tests/net/common/java/android/net/CaptivePortalTest.java @@ -44,6 +44,11 @@ public class CaptivePortalTest { } @Override + public void appRequest(final int request) throws RemoteException { + mCode = request; + } + + @Override public void logEvent(int eventId, String packageName) throws RemoteException { mCode = eventId; mPackageName = packageName; @@ -80,6 +85,12 @@ public class CaptivePortalTest { } @Test + public void testReevaluateNetwork() { + final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork()); + assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED); + } + + @Test public void testLogEvent() { final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent( MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY, diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 1e8d83c3c6cd..0ab5c97d317e 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -35,10 +35,10 @@ import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; -import android.net.NetworkFactory; import android.net.NetworkInfo; -import android.net.NetworkMisc; +import android.net.NetworkProvider; import android.net.NetworkSpecifier; import android.net.SocketKeepalive; import android.net.UidRange; @@ -114,7 +114,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) { super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag, wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore, - new NetworkMisc(), NetworkFactory.SerialNumber.NONE); + new NetworkAgentConfig(), NetworkProvider.ID_NONE); mWrapper = wrapper; } diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 535298f9b09a..9e915aec6832 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -35,10 +35,10 @@ import android.net.ConnectivityManager; import android.net.IDnsResolver; import android.net.INetd; import android.net.Network; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; -import android.net.NetworkFactory; import android.net.NetworkInfo; -import android.net.NetworkMisc; +import android.net.NetworkProvider; import android.net.NetworkScore; import android.os.INetworkManagementService; import android.text.format.DateUtils; @@ -75,7 +75,7 @@ public class LingerMonitorTest { @Mock INetd mNetd; @Mock INetworkManagementService mNMS; @Mock Context mCtx; - @Mock NetworkMisc mMisc; + @Mock NetworkAgentConfig mAgentConfig; @Mock NetworkNotificationManager mNotifier; @Mock Resources mResources; @@ -358,8 +358,8 @@ public class LingerMonitorTest { NetworkScore ns = new NetworkScore(); ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50); NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null, - caps, ns, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS, - NetworkFactory.SerialNumber.NONE); + caps, ns, mCtx, null, mAgentConfig, mConnService, mNetd, mDnsResolver, mNMS, + NetworkProvider.ID_NONE); nai.everValidated = true; return nai; } diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java index b709af1a02f1..cf70f5d499d1 100644 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java @@ -33,8 +33,8 @@ import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NetworkAgentConfig; import android.net.NetworkInfo; -import android.net.NetworkMisc; import android.os.Handler; import android.os.INetworkManagementService; import android.os.test.TestLooper; @@ -63,7 +63,7 @@ public class Nat464XlatTest { static final int NETID = 42; @Mock ConnectivityService mConnectivity; - @Mock NetworkMisc mMisc; + @Mock NetworkAgentConfig mAgentConfig; @Mock IDnsResolver mDnsResolver; @Mock INetd mNetd; @Mock INetworkManagementService mNms; @@ -93,7 +93,7 @@ public class Nat464XlatTest { mNai.networkInfo = new NetworkInfo(null); mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI); when(mNai.connService()).thenReturn(mConnectivity); - when(mNai.netMisc()).thenReturn(mMisc); + when(mNai.netAgentConfig()).thenReturn(mAgentConfig); when(mNai.handler()).thenReturn(mHandler); when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig); @@ -104,7 +104,7 @@ public class Nat464XlatTest { String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b " + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), nai.networkInfo.getDetailedState(), - mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(), + mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(), nai.linkProperties.getLinkAddresses()); assertEquals(msg, expected, Nat464Xlat.requiresClat(nai)); } @@ -113,7 +113,7 @@ public class Nat464XlatTest { String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b " + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), nai.networkInfo.getDetailedState(), - mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(), + mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(), nai.linkProperties.getLinkAddresses()); assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai)); } @@ -151,11 +151,11 @@ public class Nat464XlatTest { assertRequiresClat(true, mNai); assertShouldStartClat(true, mNai); - mMisc.skip464xlat = true; + mAgentConfig.skip464xlat = true; assertRequiresClat(false, mNai); assertShouldStartClat(false, mNai); - mMisc.skip464xlat = false; + mAgentConfig.skip464xlat = false; assertRequiresClat(true, mNai); assertShouldStartClat(true, mNai); diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 6de068e48a38..a9e0b9abba9c 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -80,6 +80,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; +import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; @@ -102,6 +103,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.TestableNetworkStatsProvider; import libcore.io.IoUtils; @@ -1001,6 +1003,88 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mService.unregisterUsageRequest(unknownRequest); } + @Test + public void testStatsProviderUpdateStats() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + final NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + + // Verifies that one requestStatsUpdate will be called during iface update. + provider.expectStatsUpdate(0 /* unused */); + + // Create some initial traffic and report to the service. + incrementCurrentTime(HOUR_IN_MILLIS); + final NetworkStats expectedStats = new NetworkStats(0L, 1) + .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 128L, 2L, 128L, 2L, 1L)) + .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, + 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 64L, 1L, 64L, 1L, 1L)); + cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats); + + // Make another empty mutable stats object. This is necessary since the new NetworkStats + // object will be used to compare with the old one in NetworkStatsRecoder, two of them + // cannot be the same object. + expectNetworkStatsUidDetail(buildEmptyStats()); + + forcePollAndWaitForIdle(); + + // Verifies that one requestStatsUpdate and setAlert will be called during polling. + provider.expectStatsUpdate(0 /* unused */); + provider.expectSetAlert(MB_IN_BYTES); + + // Verifies that service recorded history, does not verify uid tag part. + assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); + + // Verifies that onStatsUpdated updates the stats accordingly. + final NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(2, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); + + // Verifies that unregister the callback will remove the provider from service. + cb.unregister(); + forcePollAndWaitForIdle(); + provider.assertNoCallback(); + } + + @Test + public void testStatsProviderSetAlert() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)}; + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + // Simulates alert quota of the provider has been reached. + cb.onAlertReached(); + HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + + // Verifies that polling is triggered by alert reached. + provider.expectStatsUpdate(0 /* unused */); + // Verifies that global alert will be re-armed. + provider.expectSetAlert(MB_IN_BYTES); + } + private static File getBaseDir(File statsDir) { File baseDir = new File(statsDir, "netstats"); baseDir.mkdirs(); @@ -1102,6 +1186,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private void expectSettings(long persistBytes, long bucketDuration, long deleteAge) throws Exception { when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS); + when(mSettings.getPollDelay()).thenReturn(0L); when(mSettings.getSampleEnabled()).thenReturn(true); final Config config = new Config(bucketDuration, deleteAge, deleteAge); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 3623b1112bc6..469128b1e50b 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -800,7 +800,12 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config } // This is a normal reference. - return util::make_unique<Reference>(data, ref_type); + auto reference = util::make_unique<Reference>(data, ref_type); + if (res_value.dataType == android::Res_value::TYPE_DYNAMIC_REFERENCE || + res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) { + reference->is_dynamic = true; + } + return reference; } break; } diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index c016cb44af00..b08bf9a1ff17 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -109,6 +109,20 @@ TEST(ResourceUtilsTest, ParsePrivateReference) { EXPECT_TRUE(private_ref); } +TEST(ResourceUtilsTest, ParseBinaryDynamicReference) { + android::Res_value value = {}; + value.data = util::HostToDevice32(0x01); + value.dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE; + std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(ResourceType::kId, + android::ConfigDescription(), + android::ResStringPool(), value, + nullptr); + + Reference* ref = ValueCast<Reference>(item.get()); + EXPECT_TRUE(ref->is_dynamic); + EXPECT_EQ(ref->id.value().id, 0x01); +} + TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) { bool create = false; bool private_ref = false; diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index 7498e132d943..8a2f5afa7255 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -269,6 +269,11 @@ message CompoundValue { } } +// Message holding a boolean, so it can be optionally encoded. +message Boolean { + bool value = 1; +} + // A value that is a reference to another resource. This reference can be by name or resource ID. message Reference { enum Type { @@ -289,6 +294,9 @@ message Reference { // Whether this reference is referencing a private resource (@*package:type/entry). bool private = 4; + + // Whether this reference is dynamic. + Boolean is_dynamic = 5; } // A value that represents an ID. This is just a placeholder, as ID values are used to occupy a diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 4555caafb478..5b6935bafe71 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -895,7 +895,7 @@ class Linker { // android:versionCode from the framework AndroidManifest.xml. ExtractCompileSdkVersions(asset_source->GetAssetManager()); } - } else if (asset_source->IsPackageDynamic(entry.first)) { + } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) { final_table_.included_packages_[entry.first] = entry.second; } } diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index efbf636878f2..4cd6e930915d 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -634,6 +634,7 @@ static bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* o std::string* out_error) { out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type()); out_ref->private_reference = pb_ref.private_(); + out_ref->is_dynamic = pb_ref.is_dynamic().value(); if (pb_ref.id() != 0) { out_ref->id = ResourceId(pb_ref.id()); diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index e4b3fce52166..d9f6c193fc2f 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -418,6 +418,9 @@ static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) pb_ref->set_private_(ref.private_reference); pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type)); + if (ref.is_dynamic) { + pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic); + } } template <typename T> diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index e7f23302652c..61a8335e17a7 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -608,4 +608,41 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { ASSERT_FALSE(search_result.value().entry->overlayable_item); } +TEST(ProtoSerializeTest, SerializeAndDeserializeDynamicReference) { + Reference ref(ResourceId(0x00010001)); + ref.is_dynamic = true; + + pb::Item pb_item; + SerializeItemToPb(ref, &pb_item); + + ASSERT_TRUE(pb_item.has_ref()); + EXPECT_EQ(pb_item.ref().id(), ref.id.value().id); + EXPECT_TRUE(pb_item.ref().is_dynamic().value()); + + std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(), + android::ConfigDescription(), nullptr, + nullptr, nullptr); + Reference* actual_ref = ValueCast<Reference>(item.get()); + EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id); + EXPECT_TRUE(actual_ref->is_dynamic); +} + +TEST(ProtoSerializeTest, SerializeAndDeserializeNonDynamicReference) { + Reference ref(ResourceId(0x00010001)); + + pb::Item pb_item; + SerializeItemToPb(ref, &pb_item); + + ASSERT_TRUE(pb_item.has_ref()); + EXPECT_EQ(pb_item.ref().id(), ref.id.value().id); + EXPECT_FALSE(pb_item.ref().has_is_dynamic()); + + std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(), + android::ConfigDescription(), nullptr, + nullptr, nullptr); + Reference* actual_ref = ValueCast<Reference>(item.get()); + EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id); + EXPECT_FALSE(actual_ref->is_dynamic); +} + } // namespace aapt diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 83e20b5833b9..897fa80ffedb 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -245,7 +245,8 @@ std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() return package_map; } -bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { +bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId, + const std::string& package_name) const { if (packageId == 0) { return true; } @@ -253,7 +254,7 @@ bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) { for (const std::unique_ptr<const android::LoadedPackage>& loaded_package : assets->GetLoadedArsc()->GetPackages()) { - if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) { + if (package_name == loaded_package->GetPackageName() && loaded_package->IsDynamic()) { return true; } } @@ -328,12 +329,12 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( bool found = false; ResourceId res_id = 0; uint32_t type_spec_flags; + ResourceName real_name; // There can be mangled resources embedded within other packages. Here we will // look into each package and look-up the mangled name until we find the resource. asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool { - ResourceName real_name(name.package, name.type, name.entry); - + real_name = ResourceName(name.package, name.type, name.entry); if (package_name != name.package) { real_name.entry = mangled_entry; real_name.package = package_name; @@ -353,12 +354,12 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( } std::unique_ptr<SymbolTable::Symbol> s; - if (name.type == ResourceType::kAttr) { + if (real_name.type == ResourceType::kAttr) { s = LookupAttributeInTable(asset_manager_, res_id); } else { s = util::make_unique<SymbolTable::Symbol>(); s->id = res_id; - s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id()); + s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package); } if (s) { @@ -406,7 +407,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( } else { s = util::make_unique<SymbolTable::Symbol>(); s->id = id; - s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id()); + s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package); } if (s) { diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index 6997cd6714a8..06eaf63ad442 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -194,7 +194,7 @@ class AssetManagerSymbolSource : public ISymbolSource { bool AddAssetPath(const android::StringPiece& path); std::map<size_t, std::string> GetAssignedPackageIds() const; - bool IsPackageDynamic(uint32_t packageId) const; + bool IsPackageDynamic(uint32_t packageId, const std::string& package_name) const; std::unique_ptr<SymbolTable::Symbol> FindByName( const ResourceName& name) override; diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 0b82a3dcebc4..7bbac137f998 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -291,6 +291,15 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, } if (field->options().GetExtension(os::statsd::state_field_option).option() == + os::statsd::StateField::PRIMARY_FIELD_FIRST_UID) { + if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) { + errorCount++; + } else { + atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID); + } + } + + if (field->options().GetExtension(os::statsd::state_field_option).option() == os::statsd::StateField::EXCLUSIVE) { if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 3efdd520d7f5..87d4d5db0cee 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -36,6 +36,8 @@ using google::protobuf::FieldDescriptor; const int PULL_ATOM_START_ID = 10000; +const int FIRST_UID_IN_CHAIN_ID = 0; + /** * The types for atom parameters. */ diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp index 54a9982bb5c2..66ae96401974 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.cpp +++ b/tools/stats_log_api_gen/atoms_info_writer.cpp @@ -25,6 +25,8 @@ namespace android { namespace stats_log_api_gen { static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) { + fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n"); + fprintf(out, "struct StateAtomFieldOptions {\n"); fprintf(out, " std::vector<int> primaryFields;\n"); fprintf(out, " int exclusiveField;\n"); diff --git a/wifi/Android.bp b/wifi/Android.bp index 286be0b82db7..6326f14bc6fd 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -52,18 +52,43 @@ test_access_hidden_api_whitelist = [ "//external/robolectric-shadows:__subpackages__", ] +// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility +// classes before they are renamed. java_library { - name: "framework-wifi", + name: "framework-wifi-pre-jarjar", // TODO(b/140299412) should be core_current once we build against framework-system-stubs sdk_version: "core_platform", + static_libs: [ + "framework-wifi-util-lib", + "android.hardware.wifi-V1.0-java-constants", + ], libs: [ // TODO(b/140299412) should be framework-system-stubs once we fix all @hide dependencies "framework-minus-apex", - "unsupportedappusage", + "framework-annotations-lib", + "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage + "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage ], srcs: [ ":framework-wifi-updatable-sources", ], + installable: false, + visibility: [ + "//frameworks/opt/net/wifi/service", + "//frameworks/opt/net/wifi/tests/wifitests", + ], +} + +// post-jarjar version of framework-wifi +java_library { + name: "framework-wifi", + // TODO(b/140299412) should be core_current once we build against framework-system-stubs + sdk_version: "core_platform", + static_libs: [ + "framework-wifi-pre-jarjar", + ], + jarjar_rules: ":wifi-jarjar-rules", + installable: true, optimize: { enabled: false @@ -122,3 +147,8 @@ java_defaults { ], visibility: test_access_hidden_api_whitelist, } + +filegroup { + name: "wifi-jarjar-rules", + srcs: ["jarjar-rules.txt"], +} diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt new file mode 100644 index 000000000000..8f720407f4d7 --- /dev/null +++ b/wifi/jarjar-rules.txt @@ -0,0 +1,40 @@ +rule android.net.InterfaceConfigurationParcel* @0 +rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1 + +# We don't jar-jar the entire package because, we still use some classes (like +# AsyncChannel in com.android.internal.util) from these packages which are not +# inside our jar (currently in framework.jar, but will be in wifisdk.jar in the future). +rule com.android.internal.util.FastXmlSerializer* com.android.server.x.wifi.util.FastXmlSerializer@1 +rule com.android.internal.util.HexDump* com.android.server.x.wifi.util.HexDump@1 +rule com.android.internal.util.IState* com.android.server.x.wifi.util.IState@1 +rule com.android.internal.util.MessageUtils* com.android.server.x.wifi.util.MessageUtils@1 +rule com.android.internal.util.State* com.android.server.x.wifi.util.State@1 +rule com.android.internal.util.StateMachine* com.android.server.x.wifi.util.StateMachine@1 +rule com.android.internal.util.WakeupMessage* com.android.server.x.wifi.util.WakeupMessage@1 + +rule android.util.BackupUtils* com.android.server.x.wifi.util.BackupUtils@1 +rule android.util.LocalLog* com.android.server.x.wifi.util.LocalLog@1 +rule android.util.Rational* com.android.server.x.wifi.util.Rational@1 + +rule android.os.BasicShellCommandHandler* com.android.server.x.wifi.os.BasicShellCommandHandler@1 + +# Use our statically linked bouncy castle library +rule org.bouncycastle.** com.android.server.x.wifi.bouncycastle.@1 +# Use our statically linked protobuf library +rule com.google.protobuf.** com.android.server.x.wifi.protobuf.@1 +# use statically linked SystemMessageProto +rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi.messages.SystemMessageProto@1 +# Use our statically linked PlatformProperties library +rule android.sysprop.** com.android.server.x.wifi.sysprop.@1 + + +# used by both framework-wifi and wifi-service +rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1 +rule android.content.pm.ParceledListSlice* android.x.net.wifi.util.ParceledListSlice@1 +rule android.net.shared.Inet4AddressUtils* android.x.net.wifi.util.Inet4AddressUtils@1 +rule android.os.HandlerExecutor* android.x.net.wifi.util.HandlerExecutor@1 +rule android.telephony.Annotation* android.x.net.wifi.util.TelephonyAnnotation@1 +rule com.android.internal.util.AsyncChannel* android.x.net.wifi.util.AsyncChannel@1 +rule com.android.internal.util.AsyncService* android.x.net.wifi.util.AsyncService@1 +rule com.android.internal.util.Preconditions* android.x.net.wifi.util.Preconditions@1 +rule com.android.internal.util.Protocol* android.x.net.wifi.util.Protocol@1 diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 729ac14b8b73..a8a31eba303c 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -233,16 +233,20 @@ public class WifiManager { public @interface SuggestionConnectionStatusCode {} /** - * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently - * @hide + * Broadcast intent action indicating whether Wi-Fi scanning is currently available. + * Available extras: + * - {@link #EXTRA_SCAN_AVAILABLE} */ - public static final String WIFI_SCAN_AVAILABLE = "wifi_scan_available"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_WIFI_SCAN_AVAILABLE = + "android.net.wifi.action.WIFI_SCAN_AVAILABLE"; /** - * Extra int indicating scan availability, WIFI_STATE_ENABLED and WIFI_STATE_DISABLED - * @hide + * A boolean extra indicating whether scanning is currently available. + * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABLE}. + * Its value is true if scanning is currently available, false otherwise. */ - public static final String EXTRA_SCAN_AVAILABLE = "scan_enabled"; + public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE"; /** * Broadcast intent action indicating that the credential of a Wi-Fi network diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 2c39c32ac81e..4f602fac7a36 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -833,6 +833,7 @@ public class WifiScanner { * delivered to the listener. It is possible that onFullResult will not be called for all * results of the first scan if the listener was registered during the scan. * + * @param executor the Executor on which to run the callback. * @param listener specifies the object to report events to. This object is also treated as a * key for this request, and must also be specified to cancel the request. * Multiple requests should also not share this object. @@ -955,15 +956,32 @@ public class WifiScanner { * starts a single scan and reports results asynchronously * @param settings specifies various parameters for the scan; for more information look at * {@link ScanSettings} - * @param workSource WorkSource to blame for power usage * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. + * @param workSource WorkSource to blame for power usage */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { + startScan(settings, null, listener, workSource); + } + + /** + * starts a single scan and reports results asynchronously + * @param settings specifies various parameters for the scan; for more information look at + * {@link ScanSettings} + * @param executor the Executor on which to run the callback. + * @param listener specifies the object to report events to. This object is also treated as a + * key for this scan, and must also be specified to cancel the scan. Multiple + * scans should also not share this object. + * @param workSource WorkSource to blame for power usage + * @hide + */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) + public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor, + ScanListener listener, WorkSource workSource) { Objects.requireNonNull(listener, "listener cannot be null"); - int key = addListener(listener); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); Bundle scanParams = new Bundle(); @@ -1029,16 +1047,17 @@ public class WifiScanner { * {@link ScanSettings} * @param pnoSettings specifies various parameters for PNO; for more information look at * {@link PnoSettings} + * @param executor the Executor on which to run the callback. * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. * {@hide} */ public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, - PnoScanListener listener) { + @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) { Objects.requireNonNull(listener, "listener cannot be null"); Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null"); - int key = addListener(listener); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); pnoSettings.isConnected = true; @@ -1057,10 +1076,10 @@ public class WifiScanner { */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, - PnoScanListener listener) { + @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) { Objects.requireNonNull(listener, "listener cannot be null"); Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null"); - int key = addListener(listener); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); pnoSettings.isConnected = false; diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index 1af0bcbf3f30..0cc76b68a15e 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -366,7 +366,7 @@ public class WifiScannerTest { /** * Test behavior of {@link WifiScanner#startDisconnectedPnoScan(ScanSettings, PnoSettings, - * WifiScanner.PnoScanListener)} + * Executor, WifiScanner.PnoScanListener)} * @throws Exception */ @Test @@ -375,7 +375,8 @@ public class WifiScannerTest { PnoSettings pnoSettings = new PnoSettings(); WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); - mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mWifiScanner.startDisconnectedPnoScan( + scanSettings, pnoSettings, mock(Executor.class), pnoScanListener); mLooper.dispatchAll(); ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); @@ -396,7 +397,7 @@ public class WifiScannerTest { /** * Test behavior of {@link WifiScanner#startConnectedPnoScan(ScanSettings, PnoSettings, - * WifiScanner.PnoScanListener)} + * Executor, WifiScanner.PnoScanListener)} * @throws Exception */ @Test @@ -405,7 +406,8 @@ public class WifiScannerTest { PnoSettings pnoSettings = new PnoSettings(); WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); - mWifiScanner.startConnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mWifiScanner.startConnectedPnoScan( + scanSettings, pnoSettings, mock(Executor.class), pnoScanListener); mLooper.dispatchAll(); ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); @@ -426,7 +428,7 @@ public class WifiScannerTest { /** * Test behavior of {@link WifiScanner#stopPnoScan(ScanListener)} - * WifiScanner.PnoScanListener)} + * Executor, WifiScanner.PnoScanListener)} * @throws Exception */ @Test @@ -435,7 +437,8 @@ public class WifiScannerTest { PnoSettings pnoSettings = new PnoSettings(); WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); - mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mWifiScanner.startDisconnectedPnoScan( + scanSettings, pnoSettings, mock(Executor.class), pnoScanListener); mLooper.dispatchAll(); mWifiScanner.stopPnoScan(pnoScanListener); mLooper.dispatchAll(); |