summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp9
-rw-r--r--TEST_MAPPING28
-rw-r--r--apex/sdkext/Android.bp26
-rw-r--r--apex/sdkext/TEST_MAPPING2
-rw-r--r--apex/sdkext/framework/java/android/os/ext/SdkExtensions.java19
-rw-r--r--apex/sdkext/framework/tests/Android.bp11
-rw-r--r--apex/sdkext/framework/tests/AndroidManifest.xml27
-rw-r--r--apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java40
-rw-r--r--apex/sdkext/gen_sdkinfo.py19
-rw-r--r--api/current.txt18
-rw-r--r--api/system-current.txt128
-rw-r--r--api/system-lint-baseline.txt1
-rw-r--r--api/test-current.txt3
-rw-r--r--api/test-lint-baseline.txt6
-rw-r--r--cmds/statsd/src/atoms.proto17
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp8
-rw-r--r--cmds/statsd/src/state/StateTracker.h6
-rw-r--r--core/java/android/accessibilityservice/AccessibilityShortcutInfo.java10
-rw-r--r--core/java/android/annotation/Hide.java41
-rw-r--r--core/java/android/app/Activity.java110
-rw-r--r--core/java/android/app/AppOpsManager.java2
-rw-r--r--core/java/android/app/Instrumentation.java17
-rw-r--r--core/java/android/content/Intent.java172
-rw-r--r--core/java/android/content/integrity/AtomicFormula.java2
-rw-r--r--core/java/android/content/integrity/CompoundFormula.java1
-rw-r--r--core/java/android/content/integrity/Formula.java1
-rw-r--r--core/java/android/content/integrity/Rule.java1
-rw-r--r--core/java/android/content/pm/parsing/AndroidPackage.java5
-rw-r--r--core/java/android/content/pm/parsing/ApkParseUtils.java21
-rw-r--r--core/java/android/content/pm/parsing/PackageImpl.java24
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoUtils.java2
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java2
-rw-r--r--core/java/android/content/res/Configuration.java6
-rw-r--r--core/java/android/net/InterfaceConfiguration.java4
-rw-r--r--core/java/android/os/Trace.java54
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java9
-rw-r--r--core/java/android/provider/ContactsContract.java36
-rw-r--r--core/java/android/telephony/CellBroadcastIntents.java13
-rw-r--r--core/java/android/text/AlteredCharSequence.java4
-rw-r--r--core/java/android/util/LocalLog.java23
-rw-r--r--core/java/android/util/StatsEvent.java66
-rw-r--r--core/java/android/util/StatsLog.java3
-rw-r--r--core/java/android/view/Display.java9
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java51
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java133
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java23
-rw-r--r--core/java/com/android/internal/infra/AndroidFuture.java5
-rw-r--r--core/java/com/android/internal/infra/TEST_MAPPING16
-rw-r--r--core/java/com/android/internal/util/CollectionUtils.java11
-rw-r--r--core/java/com/android/internal/util/Preconditions.java18
-rw-r--r--core/java/com/android/server/SystemConfig.java6
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_media_AudioDeviceAddress.cpp53
-rw-r--r--core/jni/android_media_AudioDeviceAddress.h33
-rw-r--r--core/jni/android_media_AudioSystem.cpp63
-rw-r--r--core/jni/android_os_Trace.cpp12
-rw-r--r--core/jni/fd_utils.cpp2
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/values-television/config.xml4
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/tests/coretests/src/android/util/LocalLogTest.java20
-rw-r--r--core/tests/coretests/src/android/util/StatsEventTest.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java89
-rw-r--r--core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java126
-rw-r--r--data/etc/Android.bp2
-rw-r--r--data/etc/CleanSpec.mk2
-rw-r--r--data/etc/com.android.settings.xml1
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java3
-rw-r--r--location/java/android/location/LocationManager.java8
-rw-r--r--media/apex/java/android/media/MediaParser.java51
-rw-r--r--media/java/android/media/AudioDeviceAddress.java34
-rw-r--r--media/java/android/media/AudioDeviceInfo.java4
-rw-r--r--media/java/android/media/AudioManager.java81
-rw-r--r--media/java/android/media/AudioSystem.java42
-rw-r--r--media/java/android/media/IAudioService.aidl7
-rw-r--r--media/java/android/media/MediaFormat.java14
-rw-r--r--media/java/android/media/audiopolicy/AudioProductStrategy.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java325
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java41
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java153
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java47
-rw-r--r--packages/SystemUI/AndroidManifest.xml8
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.pngbin139 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable/tv_circle_dark.xml (renamed from packages/SystemUI/res/drawable/tv_bg_item_app_info.xml)10
-rw-r--r--packages/SystemUI/res/drawable/tv_circle_white_translucent.xml (renamed from packages/SystemUI/res/drawable/circle_red.xml)7
-rw-r--r--packages/SystemUI/res/drawable/tv_ic_mic_white.xml46
-rw-r--r--packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml26
-rw-r--r--packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml26
-rw-r--r--packages/SystemUI/res/drawable/tv_ring_white.xml (renamed from packages/SystemUI/res/drawable/tv_gradient_protection.xml)13
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_activity.xml20
-rw-r--r--packages/SystemUI/res/layout/tv_audio_recording_indicator.xml121
-rw-r--r--packages/SystemUI/res/layout/tv_item_app_info.xml41
-rw-r--r--packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml63
-rw-r--r--packages/SystemUI/res/values/attrs.xml7
-rw-r--r--packages/SystemUI/res/values/colors_tv.xml10
-rw-r--r--packages/SystemUI/res/values/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values/styles.xml1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/SysuiLog.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java376
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java25
-rw-r--r--services/Android.bp27
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java430
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java163
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java96
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java45
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java33
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java27
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java17
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java31
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java51
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java56
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java1
-rw-r--r--services/core/java/com/android/server/connectivity/DataConnectionStats.java38
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java7
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleIndexTypeIdentifier.java117
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java23
-rw-r--r--services/core/java/com/android/server/location/GnssVisibilityControl.java2
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java66
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java88
-rw-r--r--services/core/java/com/android/server/om/OverlayReferenceMapper.java375
-rw-r--r--services/core/java/com/android/server/package-info.java17
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java57
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java170
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java15
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java144
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java36
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java113
-rw-r--r--services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java3
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java14
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java18
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java22
-rw-r--r--services/net/java/android/net/NetworkMonitorManager.java2
-rw-r--r--services/net/java/android/net/ip/IpClientManager.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexTypeIdentifierTest.java218
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt208
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java178
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java66
-rw-r--r--services/tests/wmtests/Android.bp2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java39
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java4
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java17
-rw-r--r--telephony/java/android/telephony/Annotation.java2
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java30
-rw-r--r--telephony/java/android/telephony/DataFailCause.java9
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsCallSession.java14
-rw-r--r--telephony/java/com/android/ims/ImsConfig.java3
-rw-r--r--telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/IccCardConstants.java34
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneConstants.java4
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java3
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java19
-rw-r--r--telephony/java/com/android/internal/telephony/util/HandlerExecutor.java47
-rw-r--r--tests/ApkVerityTest/Android.bp2
-rw-r--r--tests/ApkVerityTest/AndroidTest.xml2
-rw-r--r--tests/ApkVerityTest/TEST_MAPPING15
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java50
-rw-r--r--tests/utils/testutils/Android.bp15
-rw-r--r--tests/utils/testutils/java/test/package-info.java21
-rw-r--r--tests/utils/testutils/java/test/util/MockitoUtils.kt70
-rw-r--r--tools/stats_log_api_gen/java_writer.cpp1
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java10
-rw-r--r--wifi/java/android/net/wifi/aware/ConfigRequest.java74
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java13
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java18
208 files changed, 5550 insertions, 1899 deletions
diff --git a/Android.bp b/Android.bp
index 7b6d145e14e5..c6966f2cd9af 100644
--- a/Android.bp
+++ b/Android.bp
@@ -445,6 +445,7 @@ java_library {
],
// For backwards compatibility.
stem: "framework",
+ apex_available: ["//apex_available:platform"],
}
// This "framework" module is NOT installed to the device. It's
@@ -465,6 +466,7 @@ java_library {
// TODO(jiyong): add stubs for APEXes here
],
sdk_version: "core_platform",
+ apex_available: ["//apex_available:platform"],
}
java_library {
@@ -473,7 +475,8 @@ java_library {
srcs: [":framework-all-sources"],
installable: false,
libs: ["app-compat-annotations"],
- static_libs: ["exoplayer2-core"]
+ static_libs: ["exoplayer2-core"],
+ apex_available: ["//apex_available:platform"],
}
java_library {
@@ -571,6 +574,7 @@ filegroup {
"core/java/android/annotation/Nullable.java",
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/IntRange.java",
+ "core/java/android/annotation/SystemApi.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
"core/java/com/android/internal/annotations/GuardedBy.java",
"core/java/com/android/internal/annotations/VisibleForTesting.java",
@@ -1683,7 +1687,8 @@ filegroup {
name: "framework-wifi-service-shared-srcs",
srcs: [
":framework-annotations",
- "core/java/android/os/HandlerExecutor.java",
+ "core/java/android/net/InterfaceConfiguration.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",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index b1c4cad72dc9..2b12da291acb 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -23,6 +23,34 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
}
],
"postsubmit-managedprofile-stress": [
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
index 40f3c45518bb..aaf25b1d40bb 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkext/Android.bp
@@ -19,7 +19,6 @@ apex {
java_libs: [ "framework-sdkext" ],
prebuilts: [
"com.android.sdkext.ldconfig",
- "cur_sdkinfo",
"derive_sdk.rc",
],
key: "com.android.sdkext.key",
@@ -43,28 +42,3 @@ prebuilt_etc {
filename: "ld.config.txt",
installable: false,
}
-
-python_binary_host {
- name: "gen_sdkinfo",
- srcs: [
- "derive_sdk/sdk.proto",
- "gen_sdkinfo.py",
- ],
- proto: {
- canonical_path_from_root: false,
- },
-}
-
-gensrcs {
- name: "cur_sdkinfo_src",
- srcs: [""],
- tools: [ "gen_sdkinfo" ],
- cmd: "$(location) -v 0 -o $(out)",
-}
-
-prebuilt_etc {
- name: "cur_sdkinfo",
- src: ":cur_sdkinfo_src",
- filename: "sdkinfo.binarypb",
- installable: false,
-}
diff --git a/apex/sdkext/TEST_MAPPING b/apex/sdkext/TEST_MAPPING
index 8dc732d36c79..91947f39980a 100644
--- a/apex/sdkext/TEST_MAPPING
+++ b/apex/sdkext/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"presubmit": [
{
- "name": "framework-sdkext-tests"
+ "name": "CtsSdkExtTestCases"
}
]
}
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
index c039a820fc04..331ef21935bb 100644
--- a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -17,13 +17,23 @@
package android.os.ext;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.os.Build.VERSION_CODES;
import android.os.SystemProperties;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/** @hide */
+/**
+ * Methods for interacting with the extension SDK.
+ *
+ * This class provides information about the extension SDK version present
+ * on this device. Use the {@link #getExtensionVersion(int) getExtension} to
+ * query for the extension version for the given SDK version.
+
+ * @hide
+ */
+@SystemApi
public class SdkExtensions {
private static final int R_EXTENSION_INT;
@@ -31,11 +41,16 @@ public class SdkExtensions {
R_EXTENSION_INT = SystemProperties.getInt("persist.com.android.sdkext.sdk_info", 0);
}
- /** Values suitable as parameters for {@link #getExtensionVersion(int)}. */
+ /**
+ * Values suitable as parameters for {@link #getExtensionVersion(int)}.
+ * @hide
+ */
@IntDef(value = { VERSION_CODES.R })
@Retention(RetentionPolicy.SOURCE)
public @interface SdkVersion {}
+ private SdkExtensions() { }
+
/**
* Return the version of the extension to the given SDK.
*
diff --git a/apex/sdkext/framework/tests/Android.bp b/apex/sdkext/framework/tests/Android.bp
deleted file mode 100644
index ab6327582efd..000000000000
--- a/apex/sdkext/framework/tests/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_test {
- name: "framework-sdkext-tests",
- srcs: ["src/**/*.java"],
- libs: [
- "android.test.base",
- "android.test.runner",
- ],
- static_libs: [ "framework-sdkext" ],
- test_suites: [ "general-tests" ],
- platform_apis: true,
-}
diff --git a/apex/sdkext/framework/tests/AndroidManifest.xml b/apex/sdkext/framework/tests/AndroidManifest.xml
deleted file mode 100644
index 831f1328c006..000000000000
--- a/apex/sdkext/framework/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.sdkext.tests">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.sdkext.tests" />
-
-</manifest>
diff --git a/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java
deleted file mode 100644
index d7dca90e0b8f..000000000000
--- a/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.ext;
-
-import android.os.Build;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-public class SdkExtensionsTest extends TestCase {
-
- @SmallTest
- public void testBadArgument() throws Exception {
- try {
- SdkExtensions.getExtensionVersion(Build.VERSION_CODES.Q);
- fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException expected) { }
- }
-
- @SmallTest
- public void testDefault() throws Exception {
- int r = SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R);
- assertTrue(r >= 0);
- }
-
-}
diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkext/gen_sdkinfo.py
deleted file mode 100644
index 5af478ba7fe6..000000000000
--- a/apex/sdkext/gen_sdkinfo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sdk_pb2
-import sys
-
-if __name__ == '__main__':
- argv = sys.argv[1:]
- if not len(argv) == 4 or sorted([argv[0], argv[2]]) != ['-o', '-v']:
- print('usage: gen_sdkinfo -v <version> -o <output-file>')
- sys.exit(1)
-
- for i in range(len(argv)):
- if sys.argv[i] == '-o':
- filename = sys.argv[i+1]
- if sys.argv[i] == '-v':
- version = int(sys.argv[i+1])
-
- proto = sdk_pb2.SdkVersion()
- proto.version = version
- with open(filename, 'wb') as f:
- f.write(proto.SerializeToString())
diff --git a/api/current.txt b/api/current.txt
index 542267c416f6..6b2f45ae118f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25367,6 +25367,8 @@ package android.media {
field public static final String KEY_COMPLEXITY = "complexity";
field public static final String KEY_CREATE_INPUT_SURFACE_SUSPENDED = "create-input-buffers-suspended";
field public static final String KEY_DURATION = "durationUs";
+ field public static final String KEY_ENCODER_DELAY = "encoder-delay";
+ field public static final String KEY_ENCODER_PADDING = "encoder-padding";
field public static final String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final String KEY_FRAME_RATE = "frame-rate";
field public static final String KEY_GRID_COLUMNS = "grid-cols";
@@ -25644,7 +25646,7 @@ package android.media {
method public void onTracksFound(int);
}
- public static interface MediaParser.SeekMap {
+ public static final class MediaParser.SeekMap {
method public long getDurationUs();
method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
method public boolean isSeekable();
@@ -38342,6 +38344,8 @@ package android.provider {
public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns {
method public static android.net.Uri getContactLookupUri(android.content.ContentResolver, android.net.Uri);
+ method @Nullable public static String getLocalAccountName(@NonNull android.content.Context);
+ method @Nullable public static String getLocalAccountType(@NonNull android.content.Context);
method public static android.content.EntityIterator newEntityIterator(android.database.Cursor);
field public static final int AGGREGATION_MODE_DEFAULT = 0; // 0x0
field public static final int AGGREGATION_MODE_DISABLED = 3; // 0x3
@@ -46534,12 +46538,12 @@ package android.telephony.mbms {
package android.text {
- public class AlteredCharSequence implements java.lang.CharSequence android.text.GetChars {
- method public char charAt(int);
- method public void getChars(int, int, char[], int);
- method public int length();
- method public static android.text.AlteredCharSequence make(CharSequence, char[], int, int);
- method public CharSequence subSequence(int, int);
+ @Deprecated public class AlteredCharSequence implements java.lang.CharSequence android.text.GetChars {
+ method @Deprecated public char charAt(int);
+ method @Deprecated public void getChars(int, int, char[], int);
+ method @Deprecated public int length();
+ method @Deprecated public static android.text.AlteredCharSequence make(CharSequence, char[], int, int);
+ method @Deprecated public CharSequence subSequence(int, int);
}
@Deprecated public class AndroidCharacter {
diff --git a/api/system-current.txt b/api/system-current.txt
index 217943c519af..dd94efd533e6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1686,9 +1686,25 @@ package android.content {
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+ field @Deprecated public static final String EXTRA_SIM_LOCKED_REASON = "reason";
+ field @Deprecated public static final String EXTRA_SIM_STATE = "ss";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
+ field @Deprecated public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+ field @Deprecated public static final String SIM_LOCKED_NETWORK = "NETWORK";
+ field @Deprecated public static final String SIM_LOCKED_ON_PIN = "PIN";
+ field @Deprecated public static final String SIM_LOCKED_ON_PUK = "PUK";
+ field @Deprecated public static final String SIM_STATE_ABSENT = "ABSENT";
+ field @Deprecated public static final String SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR";
+ field @Deprecated public static final String SIM_STATE_CARD_RESTRICTED = "CARD_RESTRICTED";
+ field @Deprecated public static final String SIM_STATE_IMSI = "IMSI";
+ field @Deprecated public static final String SIM_STATE_LOADED = "LOADED";
+ field @Deprecated public static final String SIM_STATE_LOCKED = "LOCKED";
+ field @Deprecated public static final String SIM_STATE_NOT_READY = "NOT_READY";
+ field @Deprecated public static final String SIM_STATE_PRESENT = "PRESENT";
+ field @Deprecated public static final String SIM_STATE_READY = "READY";
+ field @Deprecated public static final String SIM_STATE_UNKNOWN = "UNKNOWN";
}
public class IntentFilter implements android.os.Parcelable {
@@ -1730,7 +1746,7 @@ package android.content.integrity {
}
public abstract class AtomicFormula implements android.content.integrity.Formula {
- ctor public AtomicFormula(@android.content.integrity.AtomicFormula.Key int);
+ ctor public AtomicFormula(int);
method public int getKey();
field public static final int APP_CERTIFICATE = 1; // 0x1
field public static final int EQ = 0; // 0x0
@@ -1746,7 +1762,7 @@ package android.content.integrity {
}
public static final class AtomicFormula.BooleanAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable {
- ctor public AtomicFormula.BooleanAtomicFormula(@android.content.integrity.AtomicFormula.Key int, boolean);
+ ctor public AtomicFormula.BooleanAtomicFormula(int, boolean);
method public int describeContents();
method public int getTag();
method public boolean getValue();
@@ -1756,7 +1772,7 @@ package android.content.integrity {
}
public static final class AtomicFormula.IntAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable {
- ctor public AtomicFormula.IntAtomicFormula(@android.content.integrity.AtomicFormula.Key int, @android.content.integrity.AtomicFormula.Operator int, int);
+ ctor public AtomicFormula.IntAtomicFormula(int, int, int);
method public int describeContents();
method public int getOperator();
method public int getTag();
@@ -1766,14 +1782,8 @@ package android.content.integrity {
field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.AtomicFormula.IntAtomicFormula> CREATOR;
}
- @IntDef({android.content.integrity.AtomicFormula.PACKAGE_NAME, android.content.integrity.AtomicFormula.APP_CERTIFICATE, android.content.integrity.AtomicFormula.INSTALLER_NAME, android.content.integrity.AtomicFormula.INSTALLER_CERTIFICATE, android.content.integrity.AtomicFormula.VERSION_CODE, android.content.integrity.AtomicFormula.PRE_INSTALLED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AtomicFormula.Key {
- }
-
- @IntDef({android.content.integrity.AtomicFormula.EQ, android.content.integrity.AtomicFormula.LT, android.content.integrity.AtomicFormula.LE, android.content.integrity.AtomicFormula.GT, android.content.integrity.AtomicFormula.GE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AtomicFormula.Operator {
- }
-
public static final class AtomicFormula.StringAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable {
- ctor public AtomicFormula.StringAtomicFormula(@android.content.integrity.AtomicFormula.Key int, @NonNull String, boolean);
+ ctor public AtomicFormula.StringAtomicFormula(int, @NonNull String, boolean);
method public int describeContents();
method public boolean getIsHashedValue();
method public int getTag();
@@ -1784,9 +1794,9 @@ package android.content.integrity {
}
public final class CompoundFormula implements android.content.integrity.Formula android.os.Parcelable {
- ctor public CompoundFormula(@android.content.integrity.CompoundFormula.Connector int, @NonNull java.util.List<android.content.integrity.Formula>);
+ ctor public CompoundFormula(int, @NonNull java.util.List<android.content.integrity.Formula>);
method public int describeContents();
- method @android.content.integrity.CompoundFormula.Connector public int getConnector();
+ method public int getConnector();
method @NonNull public java.util.List<android.content.integrity.Formula> getFormulas();
method public int getTag();
method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
@@ -1797,11 +1807,8 @@ package android.content.integrity {
field public static final int OR = 1; // 0x1
}
- @IntDef({android.content.integrity.CompoundFormula.AND, android.content.integrity.CompoundFormula.OR, android.content.integrity.CompoundFormula.NOT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface CompoundFormula.Connector {
- }
-
public interface Formula {
- method @android.content.integrity.Formula.Tag public int getTag();
+ method public int getTag();
method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
method @NonNull public static android.content.integrity.Formula readFromParcel(@NonNull android.os.Parcel);
method public static void writeToParcel(@NonNull android.content.integrity.Formula, @NonNull android.os.Parcel, int);
@@ -1811,13 +1818,10 @@ package android.content.integrity {
field public static final int STRING_ATOMIC_FORMULA_TAG = 1; // 0x1
}
- @IntDef({android.content.integrity.Formula.COMPOUND_FORMULA_TAG, android.content.integrity.Formula.STRING_ATOMIC_FORMULA_TAG, android.content.integrity.Formula.INT_ATOMIC_FORMULA_TAG, android.content.integrity.Formula.BOOLEAN_ATOMIC_FORMULA_TAG}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface Formula.Tag {
- }
-
public final class Rule implements android.os.Parcelable {
- ctor public Rule(@NonNull android.content.integrity.Formula, @android.content.integrity.Rule.Effect int);
+ ctor public Rule(@NonNull android.content.integrity.Formula, int);
method public int describeContents();
- method @android.content.integrity.Rule.Effect public int getEffect();
+ method public int getEffect();
method @NonNull public android.content.integrity.Formula getFormula();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.Rule> CREATOR;
@@ -1825,9 +1829,6 @@ package android.content.integrity {
field public static final int FORCE_ALLOW = 1; // 0x1
}
- @IntDef({android.content.integrity.Rule.DENY, android.content.integrity.Rule.FORCE_ALLOW}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface Rule.Effect {
- }
-
public class RuleSet {
method @NonNull public java.util.List<android.content.integrity.Rule> getRules();
method @NonNull public String getVersion();
@@ -3928,16 +3929,19 @@ package android.media {
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAddress getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAddress);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4108,9 +4112,11 @@ package android.media.audiopolicy {
}
public final class AudioProductStrategy implements android.os.Parcelable {
+ method @NonNull public static android.media.audiopolicy.AudioProductStrategy createInvalidAudioProductStrategy(int);
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAudioAttributes();
method public int getId();
+ method public boolean supportsAudioAttributes(@NonNull android.media.AudioAttributes);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR;
}
@@ -4826,6 +4832,9 @@ package android.net.ipsec.ike {
}
public abstract class ChildSessionParams {
+ method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors();
+ method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors();
+ method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals();
}
public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
@@ -4893,6 +4902,13 @@ package android.net.ipsec.ike {
}
public final class IkeSessionParams {
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
+ method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig();
+ method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification();
+ method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals();
+ method @NonNull public java.net.InetAddress getServerAddress();
+ method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket();
}
public static final class IkeSessionParams.Builder {
@@ -4909,6 +4925,27 @@ package android.net.ipsec.ike {
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
}
+ public abstract static class IkeSessionParams.IkeAuthConfig {
+ }
+
+ public static class IkeSessionParams.IkeAuthDigitalSignLocalConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+ method @NonNull public java.security.cert.X509Certificate getClientEndCertificate();
+ method @NonNull public java.util.List<java.security.cert.X509Certificate> getIntermediateCertificates();
+ method @NonNull public java.security.PrivateKey getPrivateKey();
+ }
+
+ public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+ method @NonNull public java.security.cert.X509Certificate getRemoteCaCert();
+ }
+
+ public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+ method @NonNull public android.net.eap.EapSessionConfig getEapConfig();
+ }
+
+ public static class IkeSessionParams.IkeAuthPskConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+ method @NonNull public byte[] getPsk();
+ }
+
public final class IkeTrafficSelector {
ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
field public final int endPort;
@@ -4955,6 +4992,7 @@ package android.net.ipsec.ike {
}
public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
+ method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest> getConfigurationRequests();
}
public static final class TunnelModeChildSessionParams.Builder {
@@ -4972,6 +5010,39 @@ package android.net.ipsec.ike {
method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
}
+ public static interface TunnelModeChildSessionParams.ConfigRequest {
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ method @Nullable public java.net.Inet4Address getAddress();
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ method @Nullable public java.net.Inet4Address getAddress();
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ method @Nullable public java.net.Inet4Address getAddress();
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Subnet extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ method @Nullable public java.net.Inet6Address getAddress();
+ method public int getPrefixLength();
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ method @Nullable public java.net.Inet6Address getAddress();
+ }
+
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Subnet extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ }
+
}
package android.net.ipsec.ike.exceptions {
@@ -6731,6 +6802,14 @@ package android.os.connectivity {
}
+package android.os.ext {
+
+ public class SdkExtensions {
+ method public static int getExtensionVersion(int);
+ }
+
+}
+
package android.os.image {
public class DynamicSystemClient {
@@ -8868,6 +8947,7 @@ package android.telephony {
field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889
field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897
field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
+ field public static final int UNACCEPTABLE_NETWORK_PARAMETER = 65538; // 0x10002
field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b
field public static final int UNKNOWN = 65536; // 0x10000
field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index fcf5178716e1..9a6357572d36 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -196,7 +196,6 @@ ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.
ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
-
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/api/test-current.txt b/api/test-current.txt
index 3e144697ee28..efb85387a743 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1151,6 +1151,7 @@ package android.location {
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
+ field public static final String FUSED_PROVIDER = "fused";
}
public final class LocationRequest implements android.os.Parcelable {
@@ -4437,7 +4438,7 @@ package android.view.accessibility {
public final class AccessibilityManager {
method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, @Nullable android.os.Handler);
- method @Nullable @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public String getAccessibilityShortcutService();
+ method @NonNull @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void performAccessibilityShortcut();
method public void removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
}
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index a8c4db38c841..ef8165fc6edb 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2426,6 +2426,12 @@ ProtectedMember: android.view.View#resetResolvedDrawables():
ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
+PublicTypedef: android.os.HwParcel.Status: Don't expose @IntDef: @Status must be hidden.
+
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability: Don't expose @IntDef: @MmTelCapability must be hidden.
+
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult: Don't expose @IntDef: @ProcessCallResult must be hidden.
+
RawAidl: android.telephony.mbms.vendor.MbmsDownloadServiceBase:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f1ea4c9e0c68..a20436d4bdda 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -113,8 +113,8 @@ message Atom {
WakeupAlarmOccurred wakeup_alarm_occurred = 35;
KernelWakeupReported kernel_wakeup_reported = 36;
WifiLockStateChanged wifi_lock_state_changed = 37 [(log_from_module) = "wifi"];
- WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
- WifiScanStateChanged wifi_scan_state_changed = 39;
+ WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(log_from_module) = "wifi"];
+ WifiScanStateChanged wifi_scan_state_changed = 39 [(log_from_module) = "wifi"];
PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
SettingChanged setting_changed = 41;
ActivityForegroundStateChanged activity_foreground_state_changed = 42;
@@ -128,7 +128,7 @@ message Atom {
AppStartFullyDrawn app_start_fully_drawn = 50;
LmkKillOccurred lmk_kill_occurred = 51;
PictureInPictureStateChanged picture_in_picture_state_changed = 52;
- WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
+ WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(log_from_module) = "wifi"];
LmkStateChanged lmk_state_changed = 54;
AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
ShutdownSequenceReported shutdown_sequence_reported = 56;
@@ -7141,7 +7141,7 @@ message TextSelectionEvent {
// Event type of this event.
optional android.stats.textclassifier.EventType event_type = 2;
- // Name of the model that is involved in this event.
+ // Name of the annotator model that is involved in this event.
optional string model_name = 3;
// Type of widget that was involved in triggering this event.
@@ -7181,7 +7181,7 @@ message TextLinkifyEvent {
// Event type of this event.
optional android.stats.textclassifier.EventType event_type = 2;
- // Name of the model that is involved in this event.
+ // Name of the annotator model that is involved in this event.
optional string model_name = 3;
// Type of widget that was involved in triggering this event.
@@ -7221,7 +7221,7 @@ message ConversationActionsEvent {
// Event type of this event.
optional android.stats.textclassifier.EventType event_type = 2;
- // Name of the model that is involved in this event.
+ // Name of the actions model that is involved in this event.
optional string model_name = 3;
// Type of widget that was involved in triggering this event.
@@ -7241,6 +7241,9 @@ message ConversationActionsEvent {
// Name of source package.
optional string package_name = 9;
+
+ // Name of the annotator model that is involved in this event.
+ optional string annotator_model_name = 10;
}
/**
@@ -7255,7 +7258,7 @@ message LanguageDetectionEvent {
// Event type of this event.
optional android.stats.textclassifier.EventType event_type = 2;
- // Name of the model that is involved in this event.
+ // Name of the language detection model that is involved in this event.
optional string model_name = 3;
// Type of widget that was involved in triggering this event.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index b7d169de89c8..2c9991125d89 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -183,11 +183,9 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition
void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
StatsdStats::getInstance().noteBucketDropped(mMetricId);
- // We are going to flush the data without doing a pull first so we need to invalidte the data.
- bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
- if (pullNeeded) {
- invalidateCurrentBucket();
- }
+
+ // The current partial bucket is not flushed and does not require a pull,
+ // so the data is still valid.
flushIfNeededLocked(dropTimeNs);
clearPastBucketsLocked(dropTimeNs);
}
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index 544857f06711..e65325a52b98 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -80,10 +80,12 @@ private:
// Set of all StateListeners (objects listening for state changes)
std::set<wp<StateListener>> mListeners;
- // Reset all state values in map to default state
+ // Reset all state values in map to default state.
void handleReset(const int64_t eventTimeNs);
- // Reset only the state value mapped to primary key to default state
+ // Reset only the state value mapped to the given primary key to default state.
+ // Partial resets are used when we only need to update the state of one primary
+ // key instead of clearing/reseting every key in the map.
void handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
// Update the StateMap based on the received state value.
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index f4cadfd8bbc1..d79740b49b3d 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -141,6 +141,16 @@ public final class AccessibilityShortcutInfo {
}
/**
+ * The {@link ComponentName} of the accessibility shortcut target.
+ *
+ * @return The component name
+ */
+ @NonNull
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
* The localized summary of the accessibility shortcut target.
*
* @return The localized summary if available, and {@code null} if a summary
diff --git a/core/java/android/annotation/Hide.java b/core/java/android/annotation/Hide.java
new file mode 100644
index 000000000000..c8e5a4a97ae7
--- /dev/null
+++ b/core/java/android/annotation/Hide.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that an API is hidden by default, in a similar fashion to the
+ * <pre>@hide</pre> javadoc tag.
+ *
+ * <p>Note that, in order for this to work, metalava has to be invoked with
+ * the flag {@code --hide-annotation android.annotation.Hide}.
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
+@Retention(RetentionPolicy.CLASS)
+public @interface Hide {
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6bea68b446a4..577272e38707 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -726,7 +726,7 @@ public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
- Window.OnWindowDismissedCallback, WindowControllerCallback,
+ Window.OnWindowDismissedCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
@@ -938,6 +938,62 @@ public class Activity extends ContextThemeWrapper
private Boolean mLastDispatchedIsInMultiWindowMode;
private Boolean mLastDispatchedIsInPictureInPictureMode;
+ private final WindowControllerCallback mWindowControllerCallback =
+ new WindowControllerCallback() {
+ /**
+ * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing
+ * mode and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
+ *
+ * @hide
+ */
+ @Override
+ public void toggleFreeformWindowingMode() throws RemoteException {
+ ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken);
+ }
+
+ /**
+ * Puts the activity in picture-in-picture mode if the activity supports.
+ * @see android.R.attr#supportsPictureInPicture
+ * @hide
+ */
+ @Override
+ public void enterPictureInPictureModeIfPossible() {
+ if (mActivityInfo.supportsPictureInPicture()) {
+ enterPictureInPictureMode();
+ }
+ }
+
+ @Override
+ public boolean isTaskRoot() {
+ try {
+ return ActivityTaskManager.getService().getTaskForActivity(mToken, true) >= 0;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Update the forced status bar color.
+ * @hide
+ */
+ @Override
+ public void updateStatusBarColor(int color) {
+ mTaskDescription.setStatusBarColor(color);
+ setTaskDescription(mTaskDescription);
+ }
+
+ /**
+ * Update the forced navigation bar color.
+ * @hide
+ */
+ @Override
+ public void updateNavigationBarColor(int color) {
+ mTaskDescription.setNavigationBarColor(color);
+ setTaskDescription(mTaskDescription);
+ }
+
+ };
+
private static native String getDlWarning();
/** Return the intent that started this activity. */
@@ -3915,49 +3971,6 @@ public class Activity extends ContextThemeWrapper
/**
- * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing mode
- * and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
- *
- * @hide
- */
- @Override
- public void toggleFreeformWindowingMode() throws RemoteException {
- ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken);
- }
-
- /**
- * Update the forced status bar color.
- * @hide
- */
- @Override
- public void updateStatusBarColor(int color) {
- mTaskDescription.setStatusBarColor(color);
- setTaskDescription(mTaskDescription);
- }
-
- /**
- * Update the forced navigation bar color.
- * @hide
- */
- @Override
- public void updateNavigationBarColor(int color) {
- mTaskDescription.setNavigationBarColor(color);
- setTaskDescription(mTaskDescription);
- }
-
- /**
- * Puts the activity in picture-in-picture mode if the activity supports.
- * @see android.R.attr#supportsPictureInPicture
- * @hide
- */
- @Override
- public void enterPictureInPictureModeIfPossible() {
- if (mActivityInfo.supportsPictureInPicture()) {
- enterPictureInPictureMode();
- }
- }
-
- /**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
* this implementation for key events that should be handled normally.
@@ -6603,13 +6616,8 @@ public class Activity extends ContextThemeWrapper
*
* @return True if this is the root activity, else false.
*/
- @Override
public boolean isTaskRoot() {
- try {
- return ActivityTaskManager.getService().getTaskForActivity(mToken, true) >= 0;
- } catch (RemoteException e) {
- return false;
- }
+ return mWindowControllerCallback.isTaskRoot();
}
/**
@@ -7801,7 +7809,7 @@ public class Activity extends ContextThemeWrapper
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
- mWindow.setWindowControllerCallback(this);
+ mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5032f776f8f0..f2fa4fd6845c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2008,7 +2008,7 @@ public class AppOpsManager {
false, // WRITE_MEDIA_VIDEO
false, // READ_MEDIA_IMAGES
false, // WRITE_MEDIA_IMAGES
- false, // LEGACY_STORAGE
+ true, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
false, // ACCESS_MEDIA_LOCATION
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 588aceecf4d8..690e9567c1ef 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -493,6 +493,7 @@ public class Instrumentation {
public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
validateNotAppThread();
+ final Activity activity;
synchronized (mSync) {
intent = new Intent(intent);
@@ -527,16 +528,18 @@ public class Instrumentation {
} catch (InterruptedException e) {
}
} while (mWaitingActivities.contains(aw));
+ activity = aw.activity;
+ }
- waitForEnterAnimationComplete(aw.activity);
+ // Do not call this method within mSync, lest it could block the main thread.
+ waitForEnterAnimationComplete(activity);
- // Apply an empty transaction to ensure SF has a chance to update before
- // the Activity is ready (b/138263890).
- try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
- t.apply(true);
- }
- return aw.activity;
+ // Apply an empty transaction to ensure SF has a chance to update before
+ // the Activity is ready (b/138263890).
+ try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+ t.apply(true);
}
+ return activity;
}
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7815a330217f..ca2de6a8d1e4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4000,9 +4000,15 @@ public class Intent implements Parcelable, Cloneable {
* Broadcast Action: The sim card state has changed.
* For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here
* because TelephonyIntents is an internal class.
- * @hide
+ * The intent will have following extras.</p>
+ * <p>
+ * @see #EXTRA_SIM_STATE
+ * @see #EXTRA_SIM_LOCKED_REASON
+ *
* @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
* {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ *
+ * @hide
*/
@Deprecated
@SystemApi
@@ -4010,6 +4016,170 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
/**
+ * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for broadcasting SIM STATE.
+ * This will have one of the following intent values.
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_NOT_READY
+ * @see #SIM_STATE_ABSENT
+ * @see #SIM_STATE_PRESENT
+ * @see #SIM_STATE_CARD_IO_ERROR
+ * @see #SIM_STATE_CARD_RESTRICTED
+ * @see #SIM_STATE_LOCKED
+ * @see #SIM_STATE_READY
+ * @see #SIM_STATE_IMSI
+ * @see #SIM_STATE_LOADED
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String EXTRA_SIM_STATE = "ss";
+
+ /**
+ * The intent value UNKNOWN represents the SIM state unknown
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_UNKNOWN = "UNKNOWN";
+
+ /**
+ * The intent value NOT_READY means that the SIM is not ready eg. radio is off or powering on
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_NOT_READY = "NOT_READY";
+
+ /**
+ * The intent value ABSENT means the SIM card is missing
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_ABSENT = "ABSENT";
+
+ /**
+ * The intent value PRESENT means the device has a SIM card inserted
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_PRESENT = "PRESENT";
+
+ /**
+ * The intent value CARD_IO_ERROR means for three consecutive times there was SIM IO error
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ static public final String SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR";
+
+ /**
+ * The intent value CARD_RESTRICTED means card is present but not usable due to carrier
+ * restrictions
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ static public final String SIM_STATE_CARD_RESTRICTED = "CARD_RESTRICTED";
+
+ /**
+ * The intent value LOCKED means the SIM is locked by PIN or by network
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_LOCKED = "LOCKED";
+
+ /**
+ * The intent value READY means the SIM is ready to be accessed
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_READY = "READY";
+
+ /**
+ * The intent value IMSI means the SIM IMSI is ready in property
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_IMSI = "IMSI";
+
+ /**
+ * The intent value LOADED means all SIM records, including IMSI, are loaded
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_STATE_LOADED = "LOADED";
+
+ /**
+ * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for broadcasting SIM STATE.
+ * This extra will have one of the following intent values.
+ * <p>
+ * @see #SIM_LOCKED_ON_PIN
+ * @see #SIM_LOCKED_ON_PUK
+ * @see #SIM_LOCKED_NETWORK
+ * @see #SIM_ABSENT_ON_PERM_DISABLED
+ *
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String EXTRA_SIM_LOCKED_REASON = "reason";
+
+ /**
+ * The intent value PIN means the SIM is locked on PIN1
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_LOCKED_ON_PIN = "PIN";
+
+ /**
+ * The intent value PUK means the SIM is locked on PUK1
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ */
+ /* PUK means ICC is locked on PUK1 */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_LOCKED_ON_PUK = "PUK";
+
+ /**
+ * The intent value NETWORK means the SIM is locked on NETWORK PERSONALIZATION
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_LOCKED_NETWORK = "NETWORK";
+
+ /**
+ * The intent value PERM_DISABLED means SIM is permanently disabled due to puk fails
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+
+ /**
* Broadcast Action: indicate that the phone service state has changed.
* The intent will have the following extra values:</p>
* <p>
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index c8e164f1f232..574a93ff9355 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -44,6 +44,7 @@ public abstract class AtomicFormula implements Formula {
private static final String TAG = "AtomicFormula";
+ /** @hide */
@IntDef(
value = {
PACKAGE_NAME,
@@ -56,6 +57,7 @@ public abstract class AtomicFormula implements Formula {
@Retention(RetentionPolicy.SOURCE)
public @interface Key {}
+ /** @hide */
@IntDef(value = {EQ, LT, LE, GT, GE})
@Retention(RetentionPolicy.SOURCE)
public @interface Operator {}
diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java
index 53a99534906c..2a651d9e90d8 100644
--- a/core/java/android/content/integrity/CompoundFormula.java
+++ b/core/java/android/content/integrity/CompoundFormula.java
@@ -47,6 +47,7 @@ import java.util.Objects;
public final class CompoundFormula implements Formula, Parcelable {
private static final String TAG = "OpenFormula";
+ /** @hide */
@IntDef(
value = {
AND, OR, NOT,
diff --git a/core/java/android/content/integrity/Formula.java b/core/java/android/content/integrity/Formula.java
index 030ff6b66e1f..b092a22c0191 100644
--- a/core/java/android/content/integrity/Formula.java
+++ b/core/java/android/content/integrity/Formula.java
@@ -38,6 +38,7 @@ import java.lang.annotation.RetentionPolicy;
@SystemApi
@VisibleForTesting
public interface Formula {
+ /** @hide */
@IntDef(
value = {
COMPOUND_FORMULA_TAG,
diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java
index 914f1479edf3..39a0909321d0 100644
--- a/core/java/android/content/integrity/Rule.java
+++ b/core/java/android/content/integrity/Rule.java
@@ -42,6 +42,7 @@ import java.util.Objects;
@VisibleForTesting
public final class Rule implements Parcelable {
+ /** @hide */
@IntDef(
value = {
DENY,
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index 515185eaaf57..35df47431a91 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -229,6 +229,11 @@ public interface AndroidPackage extends Parcelable {
String getOverlayTargetName();
+ /**
+ * Map of overlayable name to actor name.
+ */
+ Map<String, String> getOverlayables();
+
// TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods?
// The refactor makes them the same value with no known consequences, so should be redundant.
String getPackageName();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index edbf73a0c0da..ffddbb6c2c47 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -52,6 +52,7 @@ import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -92,6 +93,7 @@ import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/** @hide */
@@ -287,8 +289,23 @@ public class ApkParseUtils {
+ result.getErrorMessage());
}
- return result.getResultAndNull()
- .setVolumeUuid(volumeUuid)
+ ParsingPackage pkg = result.getResultAndNull();
+ ApkAssets apkAssets = assets.getApkAssets()[0];
+ if (apkAssets.definesOverlayable()) {
+ SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
+ int size = packageNames.size();
+ for (int index = 0; index < size; index++) {
+ String packageName = packageNames.get(index);
+ Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
+ if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
+ for (String overlayable : overlayableToActor.keySet()) {
+ pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
+ }
+ }
+ }
+ }
+
+ return pkg.setVolumeUuid(volumeUuid)
.setApplicationVolumeUuid(volumeUuid)
.setSigningDetails(SigningDetails.UNKNOWN);
} catch (PackageParserException e) {
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 377279e750c6..0e736d522c10 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -18,6 +18,8 @@ package android.content.pm.parsing;
import static android.os.Build.VERSION_CODES.DONUT;
+import static java.util.Collections.emptyMap;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
@@ -55,11 +57,13 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.server.SystemConfig;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -126,6 +130,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
private String overlayCategory;
private int overlayPriority;
private boolean overlayIsStatic;
+ private Map<String, String> overlayables = emptyMap();
private String staticSharedLibName;
private long staticSharedLibVersion;
@@ -475,7 +480,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
@Override
public Map<String, ArraySet<PublicKey>> getKeySetMapping() {
- return keySetMapping == null ? Collections.emptyMap() : keySetMapping;
+ return keySetMapping == null ? emptyMap() : keySetMapping;
}
@Override
@@ -773,6 +778,13 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
}
@Override
+ public ParsingPackage addOverlayable(String overlayableName, String actorName) {
+ this.overlayables = CollectionUtils.add(this.overlayables,
+ TextUtils.safeIntern(overlayableName), TextUtils.safeIntern(actorName));
+ return this;
+ }
+
+ @Override
public PackageImpl addAdoptPermission(String adoptPermission) {
this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission);
return this;
@@ -2125,6 +2137,11 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
}
@Override
+ public Map<String, String> getOverlayables() {
+ return overlayables;
+ }
+
+ @Override
public boolean isOverlayIsStatic() {
return overlayIsStatic;
}
@@ -2291,7 +2308,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
appInfo.metaData = appMetaData;
appInfo.minAspectRatio = minAspectRatio;
appInfo.minSdkVersion = minSdkVersion;
- appInfo.name = name;
+ appInfo.name = className;
if (appInfo.name != null) {
appInfo.name = appInfo.name.trim();
}
@@ -2957,6 +2974,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
dest.writeString(this.overlayCategory);
dest.writeInt(this.overlayPriority);
dest.writeBoolean(this.overlayIsStatic);
+ dest.writeMap(this.overlayables);
dest.writeString(this.staticSharedLibName);
dest.writeLong(this.staticSharedLibVersion);
dest.writeStringList(this.libraryNames);
@@ -3100,6 +3118,8 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
this.overlayCategory = in.readString();
this.overlayPriority = in.readInt();
this.overlayIsStatic = in.readBoolean();
+ this.overlayables = new HashMap<>();
+ in.readMap(overlayables, boot);
this.staticSharedLibName = TextUtils.safeIntern(in.readString());
this.staticSharedLibVersion = in.readLong();
this.libraryNames = in.createStringArrayList();
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
index 28f0bc4e431f..af9ba8d1ed90 100644
--- a/core/java/android/content/pm/parsing/PackageInfoUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -436,7 +436,7 @@ public class PackageInfoUtils {
ii.functionalTest = i.functionalTest;
ii.sourceDir = pkg.getBaseCodePath();
- ii.publicSourceDir = pkg.getCodePath();
+ ii.publicSourceDir = pkg.getBaseCodePath();
ii.splitNames = pkg.getSplitNames();
ii.splitSourceDirs = pkg.getSplitCodePaths();
ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 43c1f6e335b0..aff1b2e05eaf 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -62,6 +62,8 @@ public interface ParsingPackage extends AndroidPackage {
ParsingPackage addOriginalPackage(String originalPackage);
+ ParsingPackage addOverlayable(String overlayableName, String actorName);
+
ParsingPackage addPermission(ParsedPermission permission);
ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 090629fe77cb..fa5ad39789dd 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -452,6 +452,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
list.add("CONFIG_SMALLEST_SCREEN_SIZE");
}
+ if ((diff & ActivityInfo.CONFIG_DENSITY) != 0) {
+ list.add("CONFIG_DENSITY");
+ }
if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
list.add("CONFIG_LAYOUT_DIRECTION");
}
@@ -461,6 +464,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
list.add("CONFIG_ASSETS_PATHS");
}
+ if ((diff & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
+ list.add("CONFIG_WINDOW_CONFIGURATION");
+ }
StringBuilder builder = new StringBuilder("{");
for (int i = 0, n = list.size(); i < n; i++) {
builder.append(list.get(i));
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index c9a999cfdf17..1ae44e169851 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -20,8 +20,6 @@ import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import com.google.android.collect.Sets;
-
import java.util.HashSet;
/**
@@ -32,7 +30,7 @@ import java.util.HashSet;
public class InterfaceConfiguration implements Parcelable {
private String mHwAddr;
private LinkAddress mAddr;
- private HashSet<String> mFlags = Sets.newHashSet();
+ private HashSet<String> mFlags = new HashSet<>();
// Must be kept in sync with constant in INetd.aidl
private static final String FLAG_UP = "up";
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 1456a734394d..e132c11d6c8e 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -21,6 +21,7 @@ import android.annotation.UnsupportedAppUsage;
import com.android.internal.os.Zygote;
+import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
/**
@@ -107,12 +108,15 @@ public final class Trace {
private static final int MAX_SECTION_NAME_LEN = 127;
// Must be volatile to avoid word tearing.
+ // This is only kept in case any apps get this by reflection but do not
+ // check the return value for null.
@UnsupportedAppUsage
private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
private static int sZygoteDebugFlags = 0;
@UnsupportedAppUsage
+ @CriticalNative
private static native long nativeGetEnabledTags();
private static native void nativeSetAppTracingAllowed(boolean allowed);
private static native void nativeSetTracingEnabled(boolean allowed);
@@ -128,47 +132,10 @@ public final class Trace {
@FastNative
private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
- static {
- // We configure two separate change callbacks, one in Trace.cpp and one here. The
- // native callback reads the tags from the system property, and this callback
- // reads the value that the native code retrieved. It's essential that the native
- // callback executes first.
- //
- // The system provides ordering through a priority level. Callbacks made through
- // SystemProperties.addChangeCallback currently have a negative priority, while
- // our native code is using a priority of zero.
- SystemProperties.addChangeCallback(() -> {
- cacheEnabledTags();
- if ((sZygoteDebugFlags & Zygote.DEBUG_JAVA_DEBUGGABLE) != 0) {
- traceCounter(TRACE_TAG_ALWAYS, "java_debuggable", 1);
- }
- });
- }
-
private Trace() {
}
/**
- * Caches a copy of the enabled-tag bits. The "master" copy is held by the native code,
- * and comes from the PROPERTY_TRACE_TAG_ENABLEFLAGS property.
- * <p>
- * If the native code hasn't yet read the property, we will cause it to do one-time
- * initialization. We don't want to do this during class init, because this class is
- * preloaded, so all apps would be stuck with whatever the zygote saw. (The zygote
- * doesn't see the system-property update broadcasts.)
- * <p>
- * We want to defer initialization until the first use by an app, post-zygote.
- * <p>
- * We're okay if multiple threads call here simultaneously -- the native state is
- * synchronized, and sEnabledTags is volatile (prevents word tearing).
- */
- private static long cacheEnabledTags() {
- long tags = nativeGetEnabledTags();
- sEnabledTags = tags;
- return tags;
- }
-
- /**
* Returns true if a trace tag is enabled.
*
* @param traceTag The trace tag to check.
@@ -178,10 +145,7 @@ public final class Trace {
*/
@UnsupportedAppUsage
public static boolean isTagEnabled(long traceTag) {
- long tags = sEnabledTags;
- if (tags == TRACE_TAG_NOT_READY) {
- tags = cacheEnabledTags();
- }
+ long tags = nativeGetEnabledTags();
return (tags & traceTag) != 0;
}
@@ -210,10 +174,6 @@ public final class Trace {
@UnsupportedAppUsage
public static void setAppTracingAllowed(boolean allowed) {
nativeSetAppTracingAllowed(allowed);
-
- // Setting whether app tracing is allowed may change the tags, so we update the cached
- // tags here.
- cacheEnabledTags();
}
/**
@@ -227,10 +187,6 @@ public final class Trace {
public static void setTracingEnabled(boolean enabled, int debugFlags) {
nativeSetTracingEnabled(enabled);
sZygoteDebugFlags = debugFlags;
-
- // Setting whether tracing is enabled may change the tags, so we update the cached tags
- // here.
- cacheEnabledTags();
}
/**
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 14c299d11a94..5cac5f580af1 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -109,4 +109,13 @@ public abstract class StorageManagerInternal {
*/
public abstract void onAppOpsChanged(int code, int uid,
@Nullable String packageName, int mode);
+
+ /**
+ * Asks the StorageManager to reset all state for the provided user; this will result
+ * in the unmounting for all volumes of the user, and, if the user is still running, the
+ * volumes will be re-mounted as well.
+ *
+ * @param userId the userId for which to reset storage
+ */
+ public abstract void resetUser(int userId);
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index f10e184ccf5a..8bf723fa54ed 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -17,6 +17,8 @@
package android.provider;
import android.accounts.Account;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -2902,6 +2904,40 @@ public final class ContactsContract {
}
/**
+ * The default value used for {@link #ACCOUNT_NAME} of raw contacts when they are inserted
+ * without a value for this column.
+ *
+ * <p>This account is used to identify contacts that are only stored locally in the
+ * contacts database instead of being associated with an {@link Account} managed by an
+ * installed application.
+ *
+ * <p>When this returns null then {@link #getLocalAccountType} will also return null and
+ * when it is non-null {@link #getLocalAccountType} will also return a non-null value.
+ */
+ @Nullable
+ public static String getLocalAccountName(@NonNull Context context) {
+ return TextUtils.nullIfEmpty(context.getString(
+ com.android.internal.R.string.config_rawContactsLocalAccountName));
+ }
+
+ /**
+ * The default value used for {@link #ACCOUNT_TYPE} of raw contacts when they are inserted
+ * without a value for this column.
+ *
+ * <p>This account is used to identify contacts that are only stored locally in the
+ * contacts database instead of being associated with an {@link Account} managed by an
+ * installed application.
+ *
+ * <p>When this returns null then {@link #getLocalAccountName} will also return null and
+ * when it is non-null {@link #getLocalAccountName} will also return a non-null value.
+ */
+ @Nullable
+ public static String getLocalAccountType(@NonNull Context context) {
+ return TextUtils.nullIfEmpty(context.getString(
+ com.android.internal.R.string.config_rawContactsLocalAccountType));
+ }
+
+ /**
* A sub-directory of a single raw contact that contains all of its
* {@link ContactsContract.Data} rows. To access this directory
* append {@link Data#CONTENT_DIRECTORY} to the raw contact URI.
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
index 4474f3ecc620..8446253c6302 100644
--- a/core/java/android/telephony/CellBroadcastIntents.java
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -26,7 +26,6 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
-import android.util.Log;
/**
* A static helper class used to send Intents with prepopulated flags.
@@ -75,20 +74,20 @@ public class CellBroadcastIntents {
@Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras) {
- Log.d(LOG_TAG, "sendOrderedBroadcastForBackgroundReceivers intent=" + intent.getAction());
int status = context.checkCallingOrSelfPermission(
"android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS");
if (status == PackageManager.PERMISSION_DENIED) {
throw new SecurityException(
"Caller does not have permission to send broadcast for background receivers");
}
- intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ Intent backgroundIntent = new Intent(intent);
+ backgroundIntent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
if (user != null) {
- context.createContextAsUser(user, 0).sendOrderedBroadcast(intent, receiverPermission,
- receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
- initialExtras);
+ context.createContextAsUser(user, 0).sendOrderedBroadcast(backgroundIntent,
+ receiverPermission, receiverAppOp, resultReceiver, scheduler, initialCode,
+ initialData, initialExtras);
} else {
- context.sendOrderedBroadcast(intent, receiverPermission,
+ context.sendOrderedBroadcast(backgroundIntent, receiverPermission,
receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
initialExtras);
}
diff --git a/core/java/android/text/AlteredCharSequence.java b/core/java/android/text/AlteredCharSequence.java
index 4cc71fd28e4b..971a47dba6e8 100644
--- a/core/java/android/text/AlteredCharSequence.java
+++ b/core/java/android/text/AlteredCharSequence.java
@@ -16,12 +16,14 @@
package android.text;
-// XXX should this really be in the public API at all?
/**
* An AlteredCharSequence is a CharSequence that is largely mirrored from
* another CharSequence, except that a specified range of characters are
* mirrored from a different char array instead.
+ *
+ * @deprecated The functionality this class offers is easily implemented outside the framework.
*/
+@Deprecated
public class AlteredCharSequence
implements CharSequence, GetChars
{
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 3a1df3ec861e..6a6bccf7052a 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -17,9 +17,11 @@
package android.util;
import android.annotation.UnsupportedAppUsage;
+import android.os.SystemClock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayDeque;
import java.util.Deque;
@@ -33,10 +35,22 @@ public final class LocalLog {
private final Deque<String> mLog;
private final int mMaxLines;
+ /**
+ * {@code true} to use log timestamps expressed in local date/time, {@code false} to use log
+ * timestamped expressed with the elapsed realtime clock and UTC system clock. {@code false} is
+ * useful when logging behavior that modifies device time zone or system clock.
+ */
+ private final boolean mUseLocalTimestamps;
+
@UnsupportedAppUsage
public LocalLog(int maxLines) {
+ this(maxLines, true /* useLocalTimestamps */);
+ }
+
+ public LocalLog(int maxLines, boolean useLocalTimestamps) {
mMaxLines = Math.max(0, maxLines);
mLog = new ArrayDeque<>(mMaxLines);
+ mUseLocalTimestamps = useLocalTimestamps;
}
@UnsupportedAppUsage
@@ -44,7 +58,14 @@ public final class LocalLog {
if (mMaxLines <= 0) {
return;
}
- append(String.format("%s - %s", LocalDateTime.now(), msg));
+ final String logLine;
+ if (mUseLocalTimestamps) {
+ logLine = String.format("%s - %s", LocalDateTime.now(), msg);
+ } else {
+ logLine = String.format(
+ "%s / %s - %s", SystemClock.elapsedRealtime(), Instant.now(), msg);
+ }
+ append(logLine);
}
private synchronized void append(String logLine) {
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
index 7e7164042781..c7659457bdf9 100644
--- a/core/java/android/util/StatsEvent.java
+++ b/core/java/android/util/StatsEvent.java
@@ -31,14 +31,23 @@ import com.android.internal.annotations.VisibleForTesting;
*
* <p>Usage:</p>
* <pre>
+ * // Pushed event
* StatsEvent statsEvent = StatsEvent.newBuilder()
* .setAtomId(atomId)
* .writeBoolean(false)
* .writeString("annotated String field")
* .addBooleanAnnotation(annotationId, true)
+ * .usePooledBuffer()
* .build();
- *
* StatsLog.write(statsEvent);
+ *
+ * // Pulled event
+ * StatsEvent statsEvent = StatsEvent.newBuilder()
+ * .setAtomId(atomId)
+ * .writeBoolean(false)
+ * .writeString("annotated String field")
+ * .addBooleanAnnotation(annotationId, true)
+ * .build();
* </pre>
* @hide
**/
@@ -210,12 +219,15 @@ public final class StatsEvent {
private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
private final int mAtomId;
- private final Buffer mBuffer;
+ private final byte[] mPayload;
+ private Buffer mBuffer;
private final int mNumBytes;
- private StatsEvent(final int atomId, @NonNull final Buffer buffer, final int numBytes) {
+ private StatsEvent(final int atomId, @Nullable final Buffer buffer,
+ @NonNull final byte[] payload, final int numBytes) {
mAtomId = atomId;
mBuffer = buffer;
+ mPayload = payload;
mNumBytes = numBytes;
}
@@ -243,7 +255,7 @@ public final class StatsEvent {
**/
@NonNull
public byte[] getBytes() {
- return mBuffer.getBytes();
+ return mPayload;
}
/**
@@ -256,10 +268,14 @@ public final class StatsEvent {
}
/**
- * Recycle this StatsEvent object.
+ * Recycle resources used by this StatsEvent object.
+ * No actions should be taken on this StatsEvent after release() is called.
**/
public void release() {
- mBuffer.release();
+ if (mBuffer != null) {
+ mBuffer.release();
+ mBuffer = null;
+ }
}
/**
@@ -280,7 +296,18 @@ public final class StatsEvent {
* optional string field3 = 3 [(annotation1) = true];
* }
*
- * // StatsEvent construction.
+ * // StatsEvent construction for pushed event.
+ * StatsEvent.newBuilder()
+ * StatsEvent statsEvent = StatsEvent.newBuilder()
+ * .setAtomId(atomId)
+ * .writeInt(3) // field1
+ * .writeLong(8L) // field2
+ * .writeString("foo") // field 3
+ * .addBooleanAnnotation(annotation1Id, true)
+ * .usePooledBuffer()
+ * .build();
+ *
+ * // StatsEvent construction for pulled event.
* StatsEvent.newBuilder()
* StatsEvent statsEvent = StatsEvent.newBuilder()
* .setAtomId(atomId)
@@ -306,6 +333,7 @@ public final class StatsEvent {
private byte mLastType;
private int mNumElements;
private int mErrorMask;
+ private boolean mUsePooledBuffer = false;
private Builder(final Buffer buffer) {
mBuffer = buffer;
@@ -569,6 +597,17 @@ public final class StatsEvent {
}
/**
+ * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent.
+ * This should be called for pushed events to reduce memory allocations and garbage
+ * collections.
+ **/
+ @NonNull
+ public Builder usePooledBuffer() {
+ mUsePooledBuffer = true;
+ return this;
+ }
+
+ /**
* Builds a StatsEvent object with values entered in this Builder.
**/
@NonNull
@@ -599,7 +638,18 @@ public final class StatsEvent {
size = mPos;
}
- return new StatsEvent(mAtomId, mBuffer, size);
+ if (mUsePooledBuffer) {
+ return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
+ } else {
+ // Create a copy of the buffer with the required number of bytes.
+ final byte[] payload = new byte[size];
+ System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size);
+
+ // Return Buffer instance to the pool.
+ mBuffer.release();
+
+ return new StatsEvent(mAtomId, null, payload, size);
+ }
}
private void writeTypeId(final byte typeId) {
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 8cb5b05df685..9ac4cf267b47 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -248,12 +248,15 @@ public final class StatsLog extends StatsLogInternal {
/**
* Write an event to stats log using the raw format encapsulated in StatsEvent.
+ * After writing to stats log, release() is called on the StatsEvent object.
+ * No further action should be taken on the StatsEvent object following this call.
*
* @param statsEvent The StatsEvent object containing the encoded buffer of data to write.
* @hide
*/
public static void write(@NonNull final StatsEvent statsEvent) {
writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
+ statsEvent.release();
}
private static void enforceDumpCallingPermission(Context context) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 03e68b0058b2..ba2509302094 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1525,5 +1525,14 @@ public final class Display {
public int describeContents() {
return 0;
}
+
+ @Override
+ public String toString() {
+ return "HdrCapabilities{"
+ + "mSupportedHdrTypes=" + Arrays.toString(mSupportedHdrTypes)
+ + ", mMaxLuminance=" + mMaxLuminance
+ + ", mMaxAverageLuminance=" + mMaxAverageLuminance
+ + ", mMinLuminance=" + mMinLuminance + '}';
+ }
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index cc2884075c39..843f8e33c66a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -117,7 +117,7 @@ public final class AccessibilityManager {
* Activity action: Launch UI to manage which accessibility service or feature is assigned
* to the navigation bar Accessibility button.
* <p>
- * Input: Nothing.
+ * Input: {@link #EXTRA_SHORTCUT_TYPE} is the shortcut type.
* </p>
* <p>
* Output: Nothing.
@@ -130,6 +130,42 @@ public final class AccessibilityManager {
"com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
/**
+ * Used as an int extra field in {@link #ACTION_CHOOSE_ACCESSIBILITY_BUTTON} intent to specify
+ * the shortcut type.
+ *
+ * @hide
+ */
+ public static final String EXTRA_SHORTCUT_TYPE =
+ "com.android.internal.intent.extra.SHORTCUT_TYPE";
+
+ /**
+ * Used as an int value for {@link #EXTRA_SHORTCUT_TYPE} to represent the accessibility button
+ * shortcut type.
+ *
+ * @hide
+ */
+ public static final int ACCESSIBILITY_BUTTON = 0;
+
+ /**
+ * Used as an int value for {@link #EXTRA_SHORTCUT_TYPE} to represent hardware key shortcut,
+ * such as volume key button.
+ *
+ * @hide
+ */
+ public static final int ACCESSIBILITY_SHORTCUT_KEY = 1;
+
+ /**
+ * Annotations for the shortcut type.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ ACCESSIBILITY_BUTTON,
+ ACCESSIBILITY_SHORTCUT_KEY
+ })
+ public @interface ShortcutType {}
+
+ /**
* Annotations for content flag of UI.
* @hide
*/
@@ -1242,27 +1278,28 @@ public final class AccessibilityManager {
}
/**
- * Get the component name of the service currently assigned to the accessibility shortcut.
+ * Returns the list of shortcut target names currently assigned to the given shortcut.
*
- * @return The flattened component name
+ * @param shortcutType The shortcut type.
+ * @return The list of shortcut target names.
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
- @Nullable
- public String getAccessibilityShortcutService() {
+ @NonNull
+ public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
}
if (service != null) {
try {
- return service.getAccessibilityShortcutService();
+ return service.getAccessibilityShortcutTargets(shortcutType);
} catch (RemoteException re) {
re.rethrowFromSystemServer();
}
}
- return null;
+ return Collections.emptyList();
}
/**
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 023fda52a1b1..36515b3ba094 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -73,7 +73,7 @@ interface IAccessibilityManager {
void performAccessibilityShortcut();
// Requires Manifest.permission.MANAGE_ACCESSIBILITY
- String getAccessibilityShortcutService();
+ List<String> getAccessibilityShortcutTargets(int shortcutType);
// System process only
boolean sendFingerprintGesture(int gestureKeyCode);
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 0b15cd06a7ea..3fdedc88fe53 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -17,6 +17,7 @@
package com.android.internal.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static com.android.internal.util.ArrayUtils.convertToLongArray;
@@ -34,6 +35,7 @@ import android.media.AudioAttributes;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.os.UserHandle;
import android.os.Vibrator;
@@ -52,11 +54,12 @@ import com.android.internal.R;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.Collections;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
- * Class to help manage the accessibility shortcut
+ * Class to help manage the accessibility shortcut key
*/
public class AccessibilityShortcutController {
private static final String TAG = "AccessibilityShortcutController";
@@ -66,6 +69,8 @@ public class AccessibilityShortcutController {
new ComponentName("com.android.server.accessibility", "ColorInversion");
public static final ComponentName DALTONIZER_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "Daltonizer");
+ public static final String MAGNIFICATION_CONTROLLER_NAME =
+ "com.android.server.accessibility.MagnificationController";
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -84,26 +89,6 @@ public class AccessibilityShortcutController {
public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
/**
- * Get the component name string for the service or feature currently assigned to the
- * accessiblity shortcut
- *
- * @param context A valid context
- * @param userId The user ID of interest
- * @return The flattened component name string of the service selected by the user, or the
- * string for the default service if the user has not made a selection
- */
- public static String getTargetServiceComponentNameString(
- Context context, int userId) {
- final String currentShortcutServiceId = Settings.Secure.getStringForUser(
- context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- userId);
- if (currentShortcutServiceId != null) {
- return currentShortcutServiceId;
- }
- return context.getString(R.string.config_defaultAccessibilityService);
- }
-
- /**
* @return An immutable map from dummy component names to feature info for toggling a framework
* feature
*/
@@ -163,7 +148,7 @@ public class AccessibilityShortcutController {
/**
* Check if the shortcut is available.
*
- * @param onLockScreen Whether or not the phone is currently locked.
+ * @param phoneLocked Whether or not the phone is currently locked.
*
* @return {@code true} if the shortcut is available
*/
@@ -172,8 +157,7 @@ public class AccessibilityShortcutController {
}
public void onSettingsChanged() {
- final boolean haveValidService =
- !TextUtils.isEmpty(getTargetServiceComponentNameString(mContext, mUserId));
+ final boolean hasShortcutTarget = hasShortcutTarget();
final ContentResolver cr = mContext.getContentResolver();
final boolean enabled = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, mUserId) == 1;
@@ -183,7 +167,7 @@ public class AccessibilityShortcutController {
mEnabledOnLockScreen = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
dialogAlreadyShown, mUserId) == 1;
- mIsShortcutEnabled = enabled && haveValidService;
+ mIsShortcutEnabled = enabled && hasShortcutTarget;
}
/**
@@ -205,7 +189,6 @@ public class AccessibilityShortcutController {
vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES);
}
-
if (dialogAlreadyShown == 0) {
// The first time, we show a warning rather than toggle the service to give the user a
// chance to turn off this feature before stuff gets enabled.
@@ -229,32 +212,44 @@ public class AccessibilityShortcutController {
mAlertDialog.dismiss();
mAlertDialog = null;
}
-
- // Show a toast alerting the user to what's happening
- final String serviceName = getShortcutFeatureDescription(false /* no summary */);
- if (serviceName == null) {
- Slog.e(TAG, "Accessibility shortcut set to invalid service");
- return;
- }
- // For accessibility services, show a toast explaining what we're doing.
- final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
- if (serviceInfo != null) {
- String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
- ? R.string.accessibility_shortcut_disabling_service
- : R.string.accessibility_shortcut_enabling_service);
- String toastMessage = String.format(toastMessageFormatString, serviceName);
- Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
- mContext, toastMessage, Toast.LENGTH_LONG);
- warningToast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- warningToast.show();
- }
-
+ showToast();
mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)
.performAccessibilityShortcut();
}
}
+ /**
+ * Show toast if current assigned shortcut target is an accessibility service and its target
+ * sdk version is less than or equal to Q, or greater than Q and does not request
+ * accessibility button.
+ */
+ private void showToast() {
+ final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+ if (serviceInfo == null) {
+ return;
+ }
+ final String serviceName = getShortcutFeatureDescription(/* no summary */ false);
+ if (serviceName == null) {
+ return;
+ }
+ final boolean requestA11yButton = (serviceInfo.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
+ .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) {
+ return;
+ }
+ // For accessibility services, show a toast explaining what we're doing.
+ String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+ ? R.string.accessibility_shortcut_disabling_service
+ : R.string.accessibility_shortcut_enabling_service);
+ String toastMessage = String.format(toastMessageFormatString, serviceName);
+ Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
+ mContext, toastMessage, Toast.LENGTH_LONG);
+ warningToast.getWindowParams().privateFlags |=
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ warningToast.show();
+ }
+
private AlertDialog createShortcutWarningDialog(int userId) {
final String serviceDescription = getShortcutFeatureDescription(true /* Include summary */);
@@ -288,25 +283,21 @@ public class AccessibilityShortcutController {
}
private AccessibilityServiceInfo getInfoForTargetService() {
- final String currentShortcutServiceString = getTargetServiceComponentNameString(
- mContext, UserHandle.USER_CURRENT);
- if (currentShortcutServiceString == null) {
+ final ComponentName targetComponentName = getShortcutTargetComponentName();
+ if (targetComponentName == null) {
return null;
}
AccessibilityManager accessibilityManager =
mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
return accessibilityManager.getInstalledServiceInfoWithComponentName(
- ComponentName.unflattenFromString(currentShortcutServiceString));
+ targetComponentName);
}
private String getShortcutFeatureDescription(boolean includeSummary) {
- final String currentShortcutServiceString = getTargetServiceComponentNameString(
- mContext, UserHandle.USER_CURRENT);
- if (currentShortcutServiceString == null) {
+ final ComponentName targetComponentName = getShortcutTargetComponentName();
+ if (targetComponentName == null) {
return null;
}
- final ComponentName targetComponentName =
- ComponentName.unflattenFromString(currentShortcutServiceString);
final ToggleableFrameworkFeatureInfo frameworkFeatureInfo =
getFrameworkShortcutFeaturesMap().get(targetComponentName);
if (frameworkFeatureInfo != null) {
@@ -372,6 +363,36 @@ public class AccessibilityShortcutController {
}
/**
+ * Returns {@code true} if any shortcut targets were assigned to accessibility shortcut key.
+ */
+ private boolean hasShortcutTarget() {
+ // AccessibilityShortcutController is initialized earlier than AccessibilityManagerService.
+ // AccessibilityManager#getAccessibilityShortcutTargets may not return correct shortcut
+ // targets during boot. Needs to read settings directly here.
+ String shortcutTargets = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId);
+ if (TextUtils.isEmpty(shortcutTargets)) {
+ shortcutTargets = mContext.getString(R.string.config_defaultAccessibilityService);
+ }
+ return !TextUtils.isEmpty(shortcutTargets);
+ }
+
+ /**
+ * Gets the component name of the shortcut target.
+ *
+ * @return The component name, or null if it's assigned by multiple targets.
+ */
+ private ComponentName getShortcutTargetComponentName() {
+ final List<String> shortcutTargets = mFrameworkObjectProvider
+ .getAccessibilityManagerInstance(mContext)
+ .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY);
+ if (shortcutTargets.size() != 1) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(shortcutTargets.get(0));
+ }
+
+ /**
* Class to wrap TextToSpeech for shortcut dialog spoken feedback.
*/
private class TtsPrompt implements TextToSpeech.OnInitListener {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9e38f3847f65..3c028d71b40d 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -32,6 +32,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -657,8 +658,9 @@ public class ResolverActivity extends Activity implements
private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
boolean filtered) {
boolean enabled = false;
+ ResolveInfo ri = null;
if (hasValidSelection) {
- ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
+ ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
if (ri == null) {
Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
return;
@@ -676,6 +678,24 @@ public class ResolverActivity extends Activity implements
.getString(R.string.activity_resolver_use_always));
}
}
+
+ ActivityInfo activityInfo = ri.activityInfo;
+
+ boolean hasRecordPermission =
+ PermissionChecker.checkPermissionForPreflight(
+ getApplicationContext(),
+ android.Manifest.permission.RECORD_AUDIO, -1,
+ activityInfo.applicationInfo.uid,
+ activityInfo.packageName)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+ if (!hasRecordPermission) {
+ // OK, we know the record permission, is this a capture device
+ boolean hasAudioCapture =
+ getIntent().getBooleanExtra(
+ ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
+ enabled = !hasAudioCapture;
+ }
mAlwaysButton.setEnabled(enabled);
}
@@ -1048,7 +1068,6 @@ public class ResolverActivity extends Activity implements
Intent startIntent = getIntent();
boolean isAudioCaptureDevice =
startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
-
return new ResolverListAdapter(context, payloadIntents, initialIntents, rList,
filterLastUsed, createListController(), useLayoutForBrowsables, this,
isAudioCaptureDevice);
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index c8929e93f030..d78bd73b080b 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -513,6 +513,11 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
try {
result = get();
} catch (Exception t) {
+ // Exceptions coming out of get() are wrapped in ExecutionException, which is not
+ // handled by Parcel.
+ if (t instanceof ExecutionException && t.getCause() instanceof Exception) {
+ t = (Exception) t.getCause();
+ }
dest.writeBoolean(true);
dest.writeException(t);
return;
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index 3781d637f68c..3de107e892b6 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -6,10 +6,18 @@
{
"name": "CtsPermissionTestCases",
"options": [
- {
- "include-filter": "android.permission.cts.PermissionControllerTest"
- }
+ {
+ "include-filter": "android.permission.cts.PermissionControllerTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.infra."
+ }
]
}
]
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 4165f202998c..4dac5427095b 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -308,6 +308,17 @@ public class CollectionUtils {
}
/**
+ * @see #add(List, Object)
+ */
+ public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) {
+ if (map == null || map == Collections.emptyMap()) {
+ map = new ArrayMap<>();
+ }
+ map.put(key, value);
+ return map;
+ }
+
+ /**
* Similar to {@link List#remove}, but with support for list values of {@code null} and
* {@link Collections#emptyList}
*/
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 731b93c18b09..3fff5c233890 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -102,6 +102,24 @@ public class Preconditions {
}
/**
+ * Ensures that an string reference passed as a parameter to the calling method is not empty.
+ *
+ * @param string an string reference
+ * @param messageTemplate a printf-style message template to use if the check fails; will be
+ * converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
+ * @return the string reference that was validated
+ * @throws IllegalArgumentException if {@code string} is empty
+ */
+ public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
+ final T string, final String messageTemplate, final Object... messageArgs) {
+ if (TextUtils.isEmpty(string)) {
+ throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
+ }
+ return string;
+ }
+
+ /**
* Ensures that an object reference passed as a parameter to the calling
* method is not null.
*
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 49a73ee7790f..e6232e851253 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -226,7 +226,7 @@ public class SystemConfig {
* Map of system pre-defined, uniquely named actors; keys are namespace,
* value maps actor name to package name.
*/
- private ArrayMap<String, ArrayMap<String, String>> mNamedActors = null;
+ private Map<String, Map<String, String>> mNamedActors = null;
public static SystemConfig getInstance() {
if (!isSystemProcess()) {
@@ -406,7 +406,7 @@ public class SystemConfig {
}
@NonNull
- public Map<String, ? extends Map<String, String>> getNamedActors() {
+ public Map<String, Map<String, String>> getNamedActors() {
return mNamedActors != null ? mNamedActors : Collections.emptyMap();
}
@@ -1063,7 +1063,7 @@ public class SystemConfig {
mNamedActors = new ArrayMap<>();
}
- ArrayMap<String, String> nameToPkgMap = mNamedActors.get(namespace);
+ Map<String, String> nameToPkgMap = mNamedActors.get(namespace);
if (nameToPkgMap == null) {
nameToPkgMap = new ArrayMap<>();
mNamedActors.put(namespace, nameToPkgMap);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 148b0a2799b4..025de5e78e14 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -152,6 +152,7 @@ cc_library_shared {
"android_util_StringBlock.cpp",
"android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
+ "android_media_AudioDeviceAddress.cpp",
"android_media_AudioEffectDescriptor.cpp",
"android_media_AudioRecord.cpp",
"android_media_AudioSystem.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 97451a2c4cfd..d92ab49ec67b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -86,6 +86,7 @@ extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
extern int register_android_hardware_UsbRequest(JNIEnv *env);
extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env);
+extern int register_android_media_AudioDeviceAddress(JNIEnv *env);
extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
@@ -1510,6 +1511,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
+ REG_JNI(register_android_media_AudioDeviceAddress),
REG_JNI(register_android_media_AudioEffectDescriptor),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioRecord),
diff --git a/core/jni/android_media_AudioDeviceAddress.cpp b/core/jni/android_media_AudioDeviceAddress.cpp
new file mode 100644
index 000000000000..5f39f7efb6a2
--- /dev/null
+++ b/core/jni/android_media_AudioDeviceAddress.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core_jni_helpers.h"
+#include "android_media_AudioDeviceAddress.h"
+#include "android_media_AudioErrors.h"
+
+#include <media/AudioDeviceTypeAddr.h>
+
+using namespace android;
+
+static jclass gAudioDeviceAddressClass;
+static jmethodID gAudioDeviceAddressCstor;
+
+namespace android {
+
+jint createAudioDeviceAddressFromNative(
+ JNIEnv *env, jobject *jAudioDeviceAddress,
+ const AudioDeviceTypeAddr *devTypeAddr) {
+ jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+ jint jNativeType = (jint)devTypeAddr->mType;
+ ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
+
+ *jAudioDeviceAddress = env->NewObject(gAudioDeviceAddressClass, gAudioDeviceAddressCstor,
+ jNativeType, jAddress.get());
+
+ return jStatus;
+}
+
+}
+
+int register_android_media_AudioDeviceAddress(JNIEnv *env)
+{
+ jclass audioDeviceTypeAddressClass = FindClassOrDie(env, "android/media/AudioDeviceAddress");
+ gAudioDeviceAddressClass = MakeGlobalRefOrDie(env, audioDeviceTypeAddressClass);
+ gAudioDeviceAddressCstor = GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>",
+ "(ILjava/lang/String;)V");
+
+ return 0;
+}
diff --git a/core/jni/android_media_AudioDeviceAddress.h b/core/jni/android_media_AudioDeviceAddress.h
new file mode 100644
index 000000000000..c66b17978776
--- /dev/null
+++ b/core/jni/android_media_AudioDeviceAddress.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_AUDIODEVICEADDRESS_H
+#define ANDROID_MEDIA_AUDIODEVICEADDRESS_H
+
+#include <system/audio.h>
+#include <media/AudioDeviceTypeAddr.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Create a Java AudioDeviceAddress instance from a C++ AudioDeviceTypeAddress
+
+extern jint createAudioDeviceAddressFromNative(JNIEnv *env, jobject *jAudioDeviceAddress,
+ const AudioDeviceTypeAddr *devTypeAddr);
+} // namespace android
+
+#endif \ No newline at end of file
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 01f9d0b0cde5..79cf0191057d 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -26,19 +26,19 @@
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
+#include "android_media_AudioAttributes.h"
+#include "android_media_AudioDeviceAddress.h"
+#include "android_media_AudioEffectDescriptor.h"
+#include "android_media_AudioErrors.h"
+#include "android_media_AudioFormat.h"
+#include "android_media_MicrophoneInfo.h"
#include <audiomanager/AudioManager.h>
-#include <media/AudioDeviceTypeAddr.h>
-#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
+#include <media/AudioSystem.h>
#include <media/MicrophoneInfo.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
-#include "android_media_AudioEffectDescriptor.h"
-#include "android_media_AudioFormat.h"
-#include "android_media_AudioErrors.h"
-#include "android_media_MicrophoneInfo.h"
-#include "android_media_AudioAttributes.h"
// ----------------------------------------------------------------------------
@@ -2254,9 +2254,9 @@ static jint
android_media_AudioSystem_setAudioHalPids(JNIEnv *env, jobject clazz, jintArray jPids)
{
if (jPids == NULL) {
- return (jint)AUDIO_JAVA_BAD_VALUE;
+ return (jint) AUDIO_JAVA_BAD_VALUE;
}
- pid_t *nPidsArray = (pid_t *)env->GetIntArrayElements(jPids, NULL);
+ pid_t *nPidsArray = (pid_t *) env->GetIntArrayElements(jPids, NULL);
std::vector<pid_t> nPids(nPidsArray, nPidsArray + env->GetArrayLength(jPids));
status_t status = AudioSystem::setAudioHalPids(nPids);
env->ReleaseIntArrayElements(jPids, nPidsArray, 0);
@@ -2270,6 +2270,48 @@ android_media_AudioSystem_isCallScreeningModeSupported(JNIEnv *env, jobject thiz
return AudioSystem::isCallScreenModeSupported();
}
+static jint
+android_media_AudioSystem_setPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint deviceType, jstring deviceAddress) {
+
+ const char *c_address = env->GetStringUTFChars(deviceAddress, NULL);
+ int status = check_AudioSystem_Command(
+ AudioSystem::setPreferredDeviceForStrategy((product_strategy_t) strategy,
+ AudioDeviceTypeAddr(deviceType, c_address)));
+ env->ReleaseStringUTFChars(deviceAddress, c_address);
+ return (jint) status;
+}
+
+static jint
+android_media_AudioSystem_removePreferredDeviceForStrategy(JNIEnv *env, jobject thiz, jint strategy)
+{
+ return (jint) check_AudioSystem_Command(
+ AudioSystem::removePreferredDeviceForStrategy((product_strategy_t) strategy));
+}
+
+static jint
+android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jobjectArray jDeviceArray)
+{
+ if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
+ ALOGE("%s invalid array to store AudioDeviceAddress", __FUNCTION__);
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ AudioDeviceTypeAddr elDevice;
+ status_t status = check_AudioSystem_Command(
+ AudioSystem::getPreferredDeviceForStrategy((product_strategy_t) strategy, elDevice));
+ if (status != NO_ERROR) {
+ return (jint) status;
+ }
+ jobject jAudioDeviceAddress = NULL;
+ jint jStatus = createAudioDeviceAddressFromNative(env, &jAudioDeviceAddress, &elDevice);
+ if (jStatus == AUDIO_JAVA_SUCCESS) {
+ env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAddress);
+ }
+ return jStatus;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2350,6 +2392,9 @@ static const JNINativeMethod gMethods[] = {
{"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled},
{"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
{"isCallScreeningModeSupported", "()Z", (void *)android_media_AudioSystem_isCallScreeningModeSupported},
+ {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
+ {"removePreferredDeviceForStrategy", "(I)I", (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
+ {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
};
static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index bd82bd91c55d..0f7611a8ead1 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -50,10 +50,6 @@ inline static void withString(JNIEnv* env, jstring jstr, F callback) {
callback(buffer.data());
}
-static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv*, jclass) {
- return atrace_get_enabled_tags();
-}
-
static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass,
jlong tag, jstring nameStr, jlong value) {
withString(env, nameStr, [tag, value](char* str) {
@@ -96,9 +92,6 @@ static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean e
static const JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
- { "nativeGetEnabledTags",
- "()J",
- (void*)android_os_Trace_nativeGetEnabledTags },
{ "nativeSetAppTracingAllowed",
"(Z)V",
(void*)android_os_Trace_nativeSetAppTracingAllowed },
@@ -123,6 +116,11 @@ static const JNINativeMethod gTraceMethods[] = {
{ "nativeAsyncTraceEnd",
"(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceEnd },
+
+ // ----------- @CriticalNative ----------------
+ { "nativeGetEnabledTags",
+ "()J",
+ (void*)atrace_get_enabled_tags },
};
int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index fd6984b830d7..0c21076065d5 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -37,6 +37,8 @@ static const char* kPathWhitelist[] = {
"/apex/com.android.ipsec/javalib/ike.jar",
"/apex/com.android.media/javalib/updatable-media.jar",
"/apex/com.android.sdkext/javalib/framework-sdkext.jar",
+ "/apex/com.android.telephony/javalib/telephony-common.jar",
+ "/apex/com.android.telephony/javalib/ims-common.jar",
"/dev/null",
"/dev/socket/zygote",
"/dev/socket/zygote_secondary",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f6e91efd2ad7..8f1114cb505e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -635,10 +635,9 @@
<protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
- <!-- NETWORK_SET_TIME / NETWORK_SET_TIMEZONE moved from com.android.phone to system server.
- They should ultimately be removed. -->
+ <!-- NETWORK_SET_TIME moved from com.android.phone to system server. It should ultimately be
+ removed. -->
<protected-broadcast android:name="android.intent.action.NETWORK_SET_TIME" />
- <protected-broadcast android:name="android.intent.action.NETWORK_SET_TIMEZONE" />
<!-- For tether entitlement recheck-->
<protected-broadcast
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 655d4dde5e57..3ecb1dddd916 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,8 +42,4 @@
<!-- Allow SystemUI to show the shutdown dialog -->
<bool name="config_showSysuiShutdown">true</bool>
-
- <!-- The time in milliseconds of prolonged user inactivity after which device goes to sleep,
- even if wakelocks are held. On TVs, this defaults to 4 hours. -->
- <integer name="config_attentiveTimeout">14400000</integer>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bfbd959df1a8..ced5deb66899 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4185,4 +4185,18 @@
<!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. -->
<string name="config_defaultBugReportHandlerApp" translatable="false"></string>
+
+ <!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this
+ column set. These contacts are stored locally on the device and will not be removed even
+ if an android.account.Account with this name and type exists. A null string will be used
+ if the value is left empty. When this is non-empty then config_rawContactsLocalAccountType
+ should also be non-empty. -->
+ <string name="config_rawContactsLocalAccountName" translatable="false"></string>
+
+ <!-- The default value used for RawContacts.ACCOUNT_TYPE when contacts are inserted without this
+ column set. These contacts are stored locally on the device and will not be removed even
+ if an android.account.Account with this name and type exists. A null string will be used
+ if the value is left empty. When this is non-empty then config_rawContactsLocalAccountName
+ should also be non-empty.-->
+ <string name="config_rawContactsLocalAccountType" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ee9287c7d64c..fc1c35879a50 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3768,4 +3768,8 @@
<java-symbol type="string" name="accessibility_system_action_toggle_split_screen_label" />
<java-symbol type="string" name="accessibility_freeform_caption" />
+
+ <!-- For contacts provider. -->
+ <java-symbol type="string" name="config_rawContactsLocalAccountName" />
+ <java-symbol type="string" name="config_rawContactsLocalAccountType" />
</resources>
diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/coretests/src/android/util/LocalLogTest.java
index 6cdcb5e542dc..d4861cd391a8 100644
--- a/core/tests/coretests/src/android/util/LocalLogTest.java
+++ b/core/tests/coretests/src/android/util/LocalLogTest.java
@@ -29,14 +29,24 @@ import java.util.List;
@LargeTest
public class LocalLogTest extends TestCase {
- public void testA() {
+ public void testA_localTimestamps() {
+ boolean localTimestamps = true;
+ doTestA(localTimestamps);
+ }
+
+ public void testA_nonLocalTimestamps() {
+ boolean localTimestamps = false;
+ doTestA(localTimestamps);
+ }
+
+ private void doTestA(boolean localTimestamps) {
String[] lines = {
- "foo",
- "bar",
- "baz"
+ "foo",
+ "bar",
+ "baz"
};
String[] want = lines;
- testcase(new LocalLog(10), lines, want);
+ testcase(new LocalLog(10, localTimestamps), lines, want);
}
public void testB() {
diff --git a/core/tests/coretests/src/android/util/StatsEventTest.java b/core/tests/coretests/src/android/util/StatsEventTest.java
index 097badadcea9..ac25e2734ac9 100644
--- a/core/tests/coretests/src/android/util/StatsEventTest.java
+++ b/core/tests/coretests/src/android/util/StatsEventTest.java
@@ -44,7 +44,7 @@ public class StatsEventTest {
@Test
public void testNoFields() {
final long minTimestamp = SystemClock.elapsedRealtimeNanos();
- final StatsEvent statsEvent = StatsEvent.newBuilder().build();
+ final StatsEvent statsEvent = StatsEvent.newBuilder().usePooledBuffer().build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
final int expectedAtomId = 0;
@@ -99,6 +99,7 @@ public class StatsEventTest {
.writeBoolean(field2)
.writeInt(field3)
.writeInt(field4)
+ .usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -167,6 +168,7 @@ public class StatsEventTest {
.writeString(field1)
.writeFloat(field2)
.writeByteArray(field3)
+ .usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -230,6 +232,7 @@ public class StatsEventTest {
.setAtomId(expectedAtomId)
.writeAttributionChain(uids, tags)
.writeLong(field2)
+ .usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -299,6 +302,7 @@ public class StatsEventTest {
final StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(expectedAtomId)
.writeKeyValuePairs(intMap, longMap, stringMap, floatMap)
+ .usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -392,6 +396,7 @@ public class StatsEventTest {
.addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
.writeBoolean(field2)
.addIntAnnotation(field2AnnotationId, field2AnnotationValue)
+ .usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 7b4054348642..82854e5b8a9d 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHO
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -49,8 +50,10 @@ import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.media.Ringtone;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
@@ -157,9 +160,12 @@ public class AccessibilityShortcutControllerTest {
when(mFrameworkObjectProvider.getRingtone(eq(mContext), any())).thenReturn(mRingtone);
when(mResources.getString(anyInt())).thenReturn("Howdy %s");
+ when(mResources.getString(R.string.config_defaultAccessibilityService)).thenReturn(null);
when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
ResolveInfo resolveInfo = mock(ResolveInfo.class);
+ resolveInfo.serviceInfo = mock(ServiceInfo.class);
+ resolveInfo.serviceInfo.applicationInfo = mApplicationInfo;
when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
when(mServiceInfo.getComponentName())
@@ -200,42 +206,47 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testShortcutAvailable_enabledButNoServiceWhenCreated_shouldReturnFalse() {
+ public void testShortcutAvailable_enabledButNoServiceWhenCreated_shouldReturnFalse()
+ throws Exception {
configureNoShortcutService();
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
assertFalse(getController().isAccessibilityShortcutAvailable(false));
}
@Test
- public void testShortcutAvailable_enabledWithValidServiceWhenCreated_shouldReturnTrue() {
+ public void testShortcutAvailable_enabledWithValidServiceWhenCreated_shouldReturnTrue()
+ throws Exception {
configureValidShortcutService();
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
assertTrue(getController().isAccessibilityShortcutAvailable(false));
}
@Test
- public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse() {
+ public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse()
+ throws Exception {
configureValidShortcutService();
configureShortcutEnabled(DISABLED_BUT_LOCK_SCREEN_ON);
assertFalse(getController().isAccessibilityShortcutAvailable(false));
}
@Test
- public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse() {
+ public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse()
+ throws Exception {
configureValidShortcutService();
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
assertFalse(getController().isAccessibilityShortcutAvailable(true));
}
@Test
- public void testShortcutAvailable_onLockScreenAndEnabledThere_shouldReturnTrue() {
+ public void testShortcutAvailable_onLockScreenAndEnabledThere_shouldReturnTrue()
+ throws Exception {
configureValidShortcutService();
configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN);
assertTrue(getController().isAccessibilityShortcutAvailable(true));
}
@Test
- public void testShortcutAvailable_onLockScreenAndLockScreenPreferenceUnset() {
+ public void testShortcutAvailable_onLockScreenAndLockScreenPreferenceUnset() throws Exception {
// When the user hasn't specified a lock screen preference, we allow from the lock screen
// as long as the user has agreed to enable the shortcut
configureValidShortcutService();
@@ -249,17 +260,19 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() {
+ public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse()
+ throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
AccessibilityShortcutController accessibilityShortcutController = getController();
- Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+ configureNoShortcutService();
accessibilityShortcutController.onSettingsChanged();
assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false));
}
@Test
- public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() {
+ public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue()
+ throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureNoShortcutService();
AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -269,7 +282,8 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse() {
+ public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse()
+ throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -279,7 +293,8 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue() {
+ public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue()
+ throws Exception {
configureShortcutEnabled(DISABLED);
configureValidShortcutService();
AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -289,7 +304,8 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testShortcutAvailable_whenLockscreenBecomesDisabled_shouldReturnFalse() {
+ public void testShortcutAvailable_whenLockscreenBecomesDisabled_shouldReturnFalse()
+ throws Exception {
configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN);
configureValidShortcutService();
AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -299,7 +315,8 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testShortcutAvailable_whenLockscreenBecomesEnabled_shouldReturnTrue() {
+ public void testShortcutAvailable_whenLockscreenBecomesEnabled_shouldReturnTrue()
+ throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -370,7 +387,7 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testClickingDisableButtonInDialog_shouldClearShortcutId() {
+ public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
@@ -458,7 +475,22 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testOnAccessibilityShortcut_showsWarningDialog_shouldTtsSpokenPrompt() {
+ public void testOnAccessibilityShortcut_sdkGreaterThanQ_reqA11yButton_callsServiceWithNoToast()
+ throws Exception {
+ configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+ configureValidShortcutService();
+ configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
+ configureRequestAccessibilityButton();
+ Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+ getController().performAccessibilityShortcut();
+
+ verifyZeroInteractions(mToast);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut();
+ }
+
+ @Test
+ public void testOnAccessibilityShortcut_showsWarningDialog_shouldTtsSpokenPrompt()
+ throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
configureTtsSpokenPromptEnabled();
@@ -482,7 +514,8 @@ public class AccessibilityShortcutControllerTest {
}
@Test
- public void testOnAccessibilityShortcut_showsWarningDialog_ttsInitFail_noSpokenPrompt() {
+ public void testOnAccessibilityShortcut_showsWarningDialog_ttsInitFail_noSpokenPrompt()
+ throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
configureTtsSpokenPromptEnabled();
@@ -500,19 +533,28 @@ public class AccessibilityShortcutControllerTest {
verify(mRingtone).play();
}
- private void configureNoShortcutService() {
+ private void configureNoShortcutService() throws Exception {
+ when(mAccessibilityManagerService
+ .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
+ .thenReturn(Collections.emptyList());
Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
}
- private void configureValidShortcutService() {
+ private void configureValidShortcutService() throws Exception {
+ when(mAccessibilityManagerService
+ .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
+ .thenReturn(Collections.singletonList(SERVICE_NAME_STRING));
Settings.Secure.putString(
mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
}
- private void configureFirstFrameworkFeature() {
+ private void configureFirstFrameworkFeature() throws Exception {
ComponentName featureComponentName =
(ComponentName) AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
.keySet().toArray()[0];
+ when(mAccessibilityManagerService
+ .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
+ .thenReturn(Collections.singletonList(featureComponentName.flattenToString()));
Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
featureComponentName.flattenToString());
}
@@ -552,6 +594,15 @@ public class AccessibilityShortcutControllerTest {
.FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK;
}
+ private void configureRequestAccessibilityButton() {
+ mServiceInfo.flags |= AccessibilityServiceInfo
+ .FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+ }
+
+ private void configureApplicationTargetSdkVersion(int versionCode) {
+ mApplicationInfo.targetSdkVersion = versionCode;
+ }
+
private void configureHandlerCallbackInvocation() {
doAnswer((InvocationOnMock invocation) -> {
Message m = (Message) invocation.getArguments()[0];
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
new file mode 100644
index 000000000000..9002c2c6b184
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.infra;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.os.Parcel;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Unit test for {@link AndroidFuture}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:com.android.internal.infra.AndroidFutureTest}
+ */
+
+@RunWith(AndroidJUnit4.class)
+public class AndroidFutureTest {
+ @Test
+ public void testGet() throws Exception {
+ AndroidFuture<Integer> future = new AndroidFuture<>();
+ future.complete(5);
+ assertThat(future.get()).isEqualTo(5);
+ }
+
+ @Test
+ public void testWhenComplete_AlreadyComplete() throws Exception {
+ AndroidFuture<Integer> future = new AndroidFuture<>();
+ future.complete(5);
+ CountDownLatch latch = new CountDownLatch(1);
+ future.whenComplete((obj, err) -> {
+ assertThat(obj).isEqualTo(5);
+ assertThat(err).isNull();
+ latch.countDown();
+ });
+ latch.await();
+ }
+
+ @Test
+ public void testWhenComplete_NotYetComplete() throws Exception {
+ AndroidFuture<Integer> future = new AndroidFuture<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ future.whenComplete((obj, err) -> {
+ assertThat(obj).isEqualTo(5);
+ assertThat(err).isNull();
+ latch.countDown();
+ });
+ assertThat(latch.getCount()).isEqualTo(1);
+ future.complete(5);
+ latch.await();
+ assertThat(latch.getCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testCompleteExceptionally() {
+ AndroidFuture<Integer> future = new AndroidFuture<>();
+ Exception origException = new UnsupportedOperationException();
+ future.completeExceptionally(origException);
+ ExecutionException executionException =
+ expectThrows(ExecutionException.class, future::get);
+ assertThat(executionException.getCause()).isSameAs(origException);
+ }
+
+ @Test
+ public void testCompleteExceptionally_Listener() throws Exception {
+ AndroidFuture<Integer> future = new AndroidFuture<>();
+ Exception origException = new UnsupportedOperationException();
+ future.completeExceptionally(origException);
+ CountDownLatch latch = new CountDownLatch(1);
+ future.whenComplete((obj, err) -> {
+ assertThat(obj).isNull();
+ assertThat(err).isSameAs(origException);
+ latch.countDown();
+ });
+ latch.await();
+ }
+
+ @Test
+ public void testWriteToParcel() throws Exception {
+ Parcel parcel = Parcel.obtain();
+ AndroidFuture<Integer> future1 = new AndroidFuture<>();
+ future1.complete(5);
+ future1.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
+ assertThat(future2.get()).isEqualTo(5);
+ }
+
+ @Test
+ public void testWriteToParcel_Exceptionally() throws Exception {
+ Parcel parcel = Parcel.obtain();
+ AndroidFuture<Integer> future1 = new AndroidFuture<>();
+ future1.completeExceptionally(new UnsupportedOperationException());
+ future1.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
+ ExecutionException executionException =
+ expectThrows(ExecutionException.class, future2::get);
+ assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
+ }
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index ff521be82599..43f65e368c10 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -88,7 +88,7 @@ prebuilt_etc {
prebuilt_etc {
name: "privapp_whitelist_com.android.launcher3",
- product_specific: true,
+ system_ext_specific: true,
sub_dir: "permissions",
src: "com.android.launcher3.xml",
filename_from_src: true,
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index 3fe421e530cb..b76eb1575b86 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -51,6 +51,8 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.provision.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.settings.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.settings.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.launcher3.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.launcher3.xml)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index ba877f8d0d02..ee989cca1787 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -51,5 +51,6 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+ <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
</privapp-permissions>
</permissions>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 6619dba159c2..8ebac667aca3 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1675,6 +1675,9 @@ public final class ImageDecoder implements AutoCloseable {
if (r == null) {
return;
}
+ if (r.width() <= 0 || r.height() <= 0) {
+ throw new IllegalStateException("Subset " + r + " is empty/unsorted");
+ }
if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
throw new IllegalStateException("Subset " + r + " not contained by "
+ "scaled image bounds: (" + width + " x " + height + ")");
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1f8c1d5352b0..aa1484f21862 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -125,6 +125,7 @@ public class LocationManager {
*
* @hide
*/
+ @TestApi
public static final String FUSED_PROVIDER = "fused";
/**
@@ -1837,12 +1838,9 @@ public class LocationManager {
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
- UnsupportedOperationException ex = new UnsupportedOperationException(
- "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) {
- throw ex;
- } else {
- Log.w(TAG, ex);
+ throw new UnsupportedOperationException(
+ "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
}
GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus();
diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java
index 8824269ea0c0..b83f44540955 100644
--- a/media/apex/java/android/media/MediaParser.java
+++ b/media/apex/java/android/media/MediaParser.java
@@ -156,19 +156,29 @@ public final class MediaParser {
* <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
* playing media samples.
*/
- public interface SeekMap {
+ public static final class SeekMap {
/** Returned by {@link #getDurationUs()} when the duration is unknown. */
- int UNKNOWN_DURATION = Integer.MIN_VALUE;
+ public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
+
+ private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
+
+ private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+ mExoPlayerSeekMap = exoplayerSeekMap;
+ }
/** Returns whether seeking is supported. */
- boolean isSeekable();
+ public boolean isSeekable() {
+ return mExoPlayerSeekMap.isSeekable();
+ }
/**
* Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
* duration is unknown.
*/
- long getDurationUs();
+ public long getDurationUs() {
+ return mExoPlayerSeekMap.getDurationUs();
+ }
/**
* Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
@@ -184,7 +194,10 @@ public final class MediaParser {
* @return The corresponding {@link SeekPoint SeekPoints}.
*/
@NonNull
- Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs);
+ public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
+ SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
+ return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
+ }
}
/** Defines a seek point in a media stream. */
@@ -647,7 +660,7 @@ public final class MediaParser {
@Override
public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
- mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap));
+ mOutputConsumer.onSeekMap(new SeekMap(exoplayerSeekMap));
}
}
@@ -764,32 +777,6 @@ public final class MediaParser {
Extractor createInstance();
}
- private static class ExoToMediaParserSeekMapAdapter implements SeekMap {
-
- private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
-
- private ExoToMediaParserSeekMapAdapter(
- com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
- mExoPlayerSeekMap = exoplayerSeekMap;
- }
-
- @Override
- public boolean isSeekable() {
- return mExoPlayerSeekMap.isSeekable();
- }
-
- @Override
- public long getDurationUs() {
- return mExoPlayerSeekMap.getDurationUs();
- }
-
- @Override
- public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
- SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
- return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
- }
- }
-
// Private static methods.
private static MediaFormat toMediaFormat(Format format) {
diff --git a/media/java/android/media/AudioDeviceAddress.java b/media/java/android/media/AudioDeviceAddress.java
index 415e77dc4049..3d8fc373006e 100644
--- a/media/java/android/media/AudioDeviceAddress.java
+++ b/media/java/android/media/AudioDeviceAddress.java
@@ -72,10 +72,12 @@ public final class AudioDeviceAddress implements Parcelable {
private final @Role int mRole;
/**
+ * @hide
* Constructor from a valid {@link AudioDeviceInfo}
* @param deviceInfo the connected audio device from which to obtain the device-identifying
* type and address.
*/
+ @SystemApi
public AudioDeviceAddress(@NonNull AudioDeviceInfo deviceInfo) {
Objects.requireNonNull(deviceInfo);
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
@@ -83,6 +85,14 @@ public final class AudioDeviceAddress implements Parcelable {
mAddress = deviceInfo.getAddress();
}
+ /**
+ * @hide
+ * Constructor from role, device type and address
+ * @param role indicates input or output role
+ * @param type the device type, as defined in {@link AudioDeviceInfo}
+ * @param address the address of the device, or an empty string for devices without one
+ */
+ @SystemApi
public AudioDeviceAddress(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
@NonNull String address) {
Objects.requireNonNull(address);
@@ -101,14 +111,38 @@ public final class AudioDeviceAddress implements Parcelable {
mAddress = address;
}
+ /*package*/ AudioDeviceAddress(int nativeType, @NonNull String address) {
+ mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
+ mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
+ mAddress = address;
+ }
+
+ /**
+ * @hide
+ * Returns the role of a device
+ * @return the role
+ */
+ @SystemApi
public @Role int getRole() {
return mRole;
}
+ /**
+ * @hide
+ * Returns the audio device type of a device
+ * @return the type, as defined in {@link AudioDeviceInfo}
+ */
+ @SystemApi
public @AudioDeviceInfo.AudioDeviceType int getType() {
return mType;
}
+ /**
+ * @hide
+ * Returns the address of the audio device, or an empty string for devices without one
+ * @return the device address
+ */
+ @SystemApi
public @NonNull String getAddress() {
return mAddress;
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index a39bc51acb24..8293b5f36c04 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -155,7 +155,9 @@ public final class AudioDeviceInfo {
TYPE_TV_TUNER }
)
@Retention(RetentionPolicy.SOURCE)
- public @interface AudioDeviceType {} /** @hide */
+ public @interface AudioDeviceType {}
+
+ /** @hide */
@IntDef(flag = false, prefix = "TYPE", value = {
TYPE_BUILTIN_MIC,
TYPE_BLUETOOTH_SCO,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d5524915f423..fac276cfb5d1 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -78,6 +78,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -1536,6 +1537,76 @@ public class AudioManager {
}
//====================================================================
+ // Audio Product Strategy routing
+
+ /**
+ * @hide
+ * Set the preferred device for a given strategy, i.e. the audio routing to be used by
+ * this audio strategy. Note that the device may not be available at the time the preferred
+ * device is set, but it will be used once made available.
+ * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
+ * this preference for this strategy.</p>
+ * @param strategy the audio strategy whose routing will be affected
+ * @param device the audio device to route to when available
+ * @return true if the operation was successful, false otherwise
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
+ @NonNull AudioDeviceAddress device) {
+ Objects.requireNonNull(strategy);
+ Objects.requireNonNull(device);
+ try {
+ final int status =
+ getService().setPreferredDeviceForStrategy(strategy.getId(), device);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Removes the preferred audio device previously set with
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAddress)}.
+ * @param strategy the audio strategy whose routing will be affected
+ * @return true if the operation was successful, false otherwise (invalid strategy, or no
+ * device set for example)
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean removePreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy) {
+ Objects.requireNonNull(strategy);
+ try {
+ final int status =
+ getService().removePreferredDeviceForStrategy(strategy.getId());
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Return the preferred device for an audio strategy, previously set with
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAddress)}
+ * @param strategy the strategy to query
+ * @return the preferred device for that strategy, or null if none was ever set or if the
+ * strategy is invalid
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @Nullable AudioDeviceAddress getPreferredDeviceForStrategy(
+ @NonNull AudioProductStrategy strategy) {
+ Objects.requireNonNull(strategy);
+ try {
+ return getService().getPreferredDeviceForStrategy(strategy.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ //====================================================================
// Offload query
/**
* Returns whether offloaded playback of an audio format is supported on the device.
@@ -4962,6 +5033,14 @@ public class AudioManager {
*/
public static final int GET_DEVICES_OUTPUTS = 0x0002;
+ /** @hide */
+ @IntDef(flag = true, prefix = "GET_DEVICES", value = {
+ GET_DEVICES_INPUTS,
+ GET_DEVICES_OUTPUTS }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioDeviceRole {}
+
/**
* Specifies to the {@link AudioManager#getDevices(int)} method to include both
* source and sink devices.
@@ -4994,7 +5073,7 @@ public class AudioManager {
* @see #GET_DEVICES_ALL
* @return A (possibly zero-length) array of AudioDeviceInfo objects.
*/
- public AudioDeviceInfo[] getDevices(int flags) {
+ public AudioDeviceInfo[] getDevices(@AudioDeviceRole int flags) {
return getDevicesStatic(flags);
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d64e4ef00164..066bf25179a6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1173,6 +1173,48 @@ public class AudioSystem
*/
public static native boolean isCallScreeningModeSupported();
+ // use case routing by product strategy
+
+ /**
+ * Sets the preferred device to use for a given audio strategy in the audio policy engine
+ * @param strategy the id of the strategy to configure
+ * @param device the device type and address to route to when available
+ * @return {@link #SUCCESS} if successfully set
+ */
+ public static int setPreferredDeviceForStrategy(
+ int strategy, @NonNull AudioDeviceAddress device) {
+ return setPreferredDeviceForStrategy(strategy,
+ AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
+ device.getAddress());
+ }
+ /**
+ * Set device routing per product strategy.
+ * @param strategy the id of the strategy to configure
+ * @param deviceType the native device type, NOT AudioDeviceInfo types
+ * @param deviceAddress the address of the device
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int setPreferredDeviceForStrategy(
+ int strategy, int deviceType, String deviceAddress);
+
+ /**
+ * Remove preferred routing for the strategy
+ * @param strategy the id of the strategy to configure
+ * @return {@link #SUCCESS} if successfully removed
+ */
+ public static native int removePreferredDeviceForStrategy(int strategy);
+
+ /**
+ * Query previously set preferred device for a strategy
+ * @param strategy the id of the strategy to query for
+ * @param device an array of size 1 that will contain the preferred device, or null if
+ * none was set
+ * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
+ * and written to the array
+ */
+ public static native int getPreferredDeviceForStrategy(int strategy,
+ AudioDeviceAddress[] device);
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ef451ce401e6..ad7335e5b683 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,6 +18,7 @@ package android.media;
import android.bluetooth.BluetoothDevice;
import android.media.AudioAttributes;
+import android.media.AudioDeviceAddress;
import android.media.AudioFocusInfo;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
@@ -265,6 +266,12 @@ interface IAudioService {
boolean isCallScreeningModeSupported();
+ int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAddress device);
+
+ int removePreferredDeviceForStrategy(in int strategy);
+
+ AudioDeviceAddress getPreferredDeviceForStrategy(in int strategy);
+
// WARNING: read warning at top of file, new methods that need to be used by native
// code via IAudioManager.h need to be added to the top section.
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 8080f45642dc..94a6f134a1b6 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -99,6 +99,8 @@ import java.util.stream.Collectors;
* <tr><td>{@link #KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the maximum number of channels the decoder outputs.</td></tr>
* <tr><td>{@link #KEY_AAC_DRC_EFFECT_TYPE}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the MPEG-D DRC effect type to use.</td></tr>
* <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>optional, a mask of audio channel assignments</td></tr>
+ * <tr><td>{@link #KEY_ENCODER_DELAY}</td><td>Integer</td><td>optional, the number of frames to trim from the start of the decoded audio stream.</td></tr>
+ * <tr><td>{@link #KEY_ENCODER_PADDING}</td><td>Integer</td><td>optional, the number of frames to trim from the end of the decoded audio stream.</td></tr>
* <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>
* </table>
*
@@ -569,6 +571,18 @@ public final class MediaFormat {
public static final String KEY_CHANNEL_MASK = "channel-mask";
/**
+ * A key describing the number of frames to trim from the start of the decoded audio stream.
+ * The associated value is an integer.
+ */
+ public static final String KEY_ENCODER_DELAY = "encoder-delay";
+
+ /**
+ * A key describing the number of frames to trim from the end of the decoded audio stream.
+ * The associated value is an integer.
+ */
+ public static final String KEY_ENCODER_PADDING = "encoder-padding";
+
+ /**
* A key describing the AAC profile to be used (AAC audio formats only).
* Constants are declared in {@link android.media.MediaCodecInfo.CodecProfileLevel}.
*/
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 2799d46cbb47..612f83a14e12 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -19,6 +19,7 @@ package android.media.audiopolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.media.MediaRecorder;
@@ -82,6 +83,18 @@ public final class AudioProductStrategy implements Parcelable {
/**
* @hide
+ * Create an invalid AudioProductStrategy instance for testing
+ * @param id the ID for the invalid strategy, always use a different one than in use
+ * @return an invalid instance that cannot successfully be used for volume groups or routing
+ */
+ @TestApi
+ @SystemApi
+ public static @NonNull AudioProductStrategy createInvalidAudioProductStrategy(int id) {
+ return new AudioProductStrategy("dummy strategy", id, new AudioAttributesGroup[0]);
+ }
+
+ /**
+ * @hide
* @param streamType to match against AudioProductStrategy
* @return the AudioAttributes for the first strategy found with the associated stream type
* If no match is found, returns AudioAttributes with unknown content_type and usage
@@ -222,6 +235,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return true if the {@link AudioProductStrategy} supports the given {@link AudioAttributes},
* false otherwise.
*/
+ @SystemApi
public boolean supportsAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 96aee512c090..a2bd210b67a6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -155,8 +155,8 @@ public class A2dpProfile implements LocalBluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (mService == null) return false;
// Downgrade priority as user is disconnecting the headset.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -179,23 +179,29 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return false;
+ }
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
- return mService.getPriority(device);
+ if (mService == null) {
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
boolean isA2dpPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 55765dd40d36..9c896c80b409 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -124,8 +124,8 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
return false;
}
// Downgrade priority as user is disconnecting the headset.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -141,14 +141,14 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -156,11 +156,11 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 9f7b7181b19f..560cb3b9b5b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -120,8 +120,8 @@ public class HeadsetProfile implements LocalBluetoothProfile {
return false;
}
// Downgrade priority as user is disconnecting the headset.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -165,14 +165,14 @@ public class HeadsetProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -180,11 +180,11 @@ public class HeadsetProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index ebaeb742b198..58655a2b930a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -153,8 +153,8 @@ public class HearingAidProfile implements LocalBluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (mService == null) return false;
// Downgrade priority as user is disconnecting the hearing aid.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -177,23 +177,29 @@ public class HearingAidProfile implements LocalBluetoothProfile {
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return false;
+ }
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
- return mService.getPriority(device);
+ if (mService == null) {
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 860b77d1ebcd..a372e23654e0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -135,8 +135,8 @@ final class HfpClientProfile implements LocalBluetoothProfile {
return false;
}
// Downgrade priority as user is disconnecting the headset.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -154,15 +154,15 @@ final class HfpClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
@Override
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
@Override
@@ -171,11 +171,11 @@ final class HfpClientProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 6d874ab2be9b..975a1e67af5b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -116,23 +116,27 @@ public class HidProfile implements LocalBluetoothProfile {
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.getPriority(device) != BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return false;
+ }
+ return mService.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
- return mService.getPriority(device);
+ if (mService == null) {
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
if (mService == null) return;
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index d4dda329fa63..95139a1bfab9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -123,8 +123,8 @@ public final class MapClientProfile implements LocalBluetoothProfile {
return false;
}
// Downgrade priority as user is disconnecting.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -140,14 +140,14 @@ public final class MapClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -155,11 +155,11 @@ public final class MapClientProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index b2a9a6a28231..31a0eea56b42 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -119,8 +119,8 @@ public class MapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -136,14 +136,14 @@ public class MapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -151,11 +151,11 @@ public class MapProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index e1e5dbe29a1a..8e3f3edcef10 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -57,7 +57,7 @@ final class OppProfile implements LocalBluetoothProfile {
}
public int getPreferred(BluetoothDevice device) {
- return BluetoothProfile.PRIORITY_OFF; // Settings app doesn't handle OPP
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; // Settings app doesn't handle OPP
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index a2da4fb30e39..4ea0df621bea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -151,14 +151,14 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -166,11 +166,11 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 9b733f2e0e50..0ca4d6195a32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -119,8 +119,8 @@ final class SapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
return mService.disconnect(device);
}
@@ -136,14 +136,14 @@ final class SapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
- return mService.getPriority(device);
+ return mService.getConnectionPolicy(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -151,11 +151,11 @@ final class SapProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index b0bdf1dd8594..328bfb281992 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -188,6 +188,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
static final String KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS =
"key_subscription_expiration_time_in_millis";
static final String KEY_PASSPOINT_CONFIGURATION_VERSION = "key_passpoint_configuration_version";
+ static final String KEY_IS_PSK_SAE_TRANSITION_MODE = "key_is_psk_sae_transition_mode";
+ static final String KEY_IS_OWE_TRANSITION_MODE = "key_is_owe_transition_mode";
static final AtomicInteger sLastId = new AtomicInteger(0);
/*
@@ -202,15 +204,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
public static final int SECURITY_OWE = 4;
public static final int SECURITY_SAE = 5;
public static final int SECURITY_EAP_SUITE_B = 6;
- public static final int SECURITY_PSK_SAE_TRANSITION = 7;
- public static final int SECURITY_OWE_TRANSITION = 8;
- public static final int SECURITY_MAX_VAL = 9; // Has to be the last
+ public static final int SECURITY_MAX_VAL = 7; // Has to be the last
private static final int PSK_UNKNOWN = 0;
private static final int PSK_WPA = 1;
private static final int PSK_WPA2 = 2;
private static final int PSK_WPA_WPA2 = 3;
- private static final int PSK_SAE = 4;
private static final int EAP_UNKNOWN = 0;
private static final int EAP_WPA = 1; // WPA-EAP
@@ -274,6 +273,9 @@ public class AccessPoint implements Comparable<AccessPoint> {
private String mOsuFailure;
private boolean mOsuProvisioningComplete = false;
+ private boolean mIsPskSaeTransitionMode = false;
+ private boolean mIsOweTransitionMode = false;
+
/**
* The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
*/
@@ -344,6 +346,13 @@ public class AccessPoint implements Comparable<AccessPoint> {
if (savedState.containsKey(KEY_PASSPOINT_CONFIGURATION_VERSION)) {
mPasspointConfigurationVersion = savedState.getInt(KEY_PASSPOINT_CONFIGURATION_VERSION);
}
+ if (savedState.containsKey(KEY_IS_PSK_SAE_TRANSITION_MODE)) {
+ mIsPskSaeTransitionMode = savedState.getBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE);
+ }
+ if (savedState.containsKey(KEY_IS_OWE_TRANSITION_MODE)) {
+ mIsOweTransitionMode = savedState.getBoolean(KEY_IS_OWE_TRANSITION_MODE);
+ }
+
update(mConfig, mInfo, mNetworkInfo);
// Calculate required fields
@@ -675,8 +684,15 @@ public class AccessPoint implements Comparable<AccessPoint> {
return oldMetering == mIsScoredNetworkMetered;
}
- public static String getKey(ScanResult result) {
- return getKey(result.SSID, result.BSSID, getSecurity(result));
+ /**
+ * Generates an AccessPoint key for a given scan result
+ *
+ * @param context
+ * @param result Scan result
+ * @return AccessPoint key
+ */
+ public static String getKey(Context context, ScanResult result) {
+ return getKey(result.SSID, result.BSSID, getSecurity(context, result));
}
/**
@@ -734,7 +750,42 @@ public class AccessPoint implements Comparable<AccessPoint> {
* Determines if the other AccessPoint represents the same network as this AccessPoint
*/
public boolean matches(AccessPoint other) {
- return getKey().equals(other.getKey());
+ if (isPasspoint() || isPasspointConfig() || isOsuProvider()) {
+ return getKey().equals(other.getKey());
+ }
+
+ if (!isSameSsidOrBssid(other)) {
+ return false;
+ }
+
+ final int otherApSecurity = other.getSecurity();
+ if (mIsPskSaeTransitionMode) {
+ if (otherApSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) {
+ return true;
+ } else if (otherApSecurity == SECURITY_PSK) {
+ return true;
+ }
+ } else {
+ if ((security == SECURITY_SAE || security == SECURITY_PSK)
+ && other.isPskSaeTransitionMode()) {
+ return true;
+ }
+ }
+
+ if (mIsOweTransitionMode) {
+ if (otherApSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) {
+ return true;
+ } else if (otherApSecurity == SECURITY_NONE) {
+ return true;
+ }
+ } else {
+ if ((security == SECURITY_OWE || security == SECURITY_NONE)
+ && other.isOweTransitionMode()) {
+ return true;
+ }
+ }
+
+ return security == other.getSecurity();
}
public boolean matches(WifiConfiguration config) {
@@ -748,18 +799,77 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
final int configSecurity = getSecurity(config);
- final WifiManager wifiManager = getWifiManager();
- switch (security) {
- case SECURITY_PSK_SAE_TRANSITION:
- return configSecurity == SECURITY_PSK
- || (wifiManager.isWpa3SaeSupported() && configSecurity == SECURITY_SAE);
- case SECURITY_OWE_TRANSITION:
- return configSecurity == SECURITY_NONE
- || (wifiManager.isEnhancedOpenSupported()
- && configSecurity == SECURITY_OWE);
- default:
- return security == configSecurity;
+ if (mIsPskSaeTransitionMode) {
+ if (configSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) {
+ return true;
+ } else if (configSecurity == SECURITY_PSK) {
+ return true;
+ }
+ }
+
+ if (mIsOweTransitionMode) {
+ if (configSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) {
+ return true;
+ } else if (configSecurity == SECURITY_NONE) {
+ return true;
+ }
}
+
+ return security == getSecurity(config);
+ }
+
+ private boolean matches(WifiConfiguration config, WifiInfo wifiInfo) {
+ if (config == null || wifiInfo == null) {
+ return false;
+ }
+ if (!config.isPasspoint() && !isSameSsidOrBssid(wifiInfo)) {
+ return false;
+ }
+ return matches(config);
+ }
+
+ @VisibleForTesting
+ boolean matches(ScanResult scanResult) {
+ if (scanResult == null) {
+ return false;
+ }
+ if (isPasspoint() || isOsuProvider()) {
+ throw new IllegalStateException("Should not matches a Passpoint by ScanResult");
+ }
+
+ if (!isSameSsidOrBssid(scanResult)) {
+ return false;
+ }
+
+ if (mIsPskSaeTransitionMode) {
+ if (scanResult.capabilities.contains("SAE")
+ && getWifiManager().isWpa3SaeSupported()) {
+ return true;
+ } else if (scanResult.capabilities.contains("PSK")) {
+ return true;
+ }
+ } else {
+ if ((security == SECURITY_SAE || security == SECURITY_PSK)
+ && AccessPoint.isPskSaeTransitionMode(scanResult)) {
+ return true;
+ }
+ }
+
+ if (mIsOweTransitionMode) {
+ final int scanResultSccurity = getSecurity(mContext, scanResult);
+ if (scanResultSccurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) {
+ return true;
+ } else if (scanResultSccurity == SECURITY_NONE) {
+ return true;
+ }
+ } else {
+ if ((security == SECURITY_OWE || security == SECURITY_NONE)
+ && AccessPoint.isOweTransitionMode(scanResult)) {
+ return true;
+ }
+ }
+
+ return security == getSecurity(mContext, scanResult);
}
public WifiConfiguration getConfig() {
@@ -846,14 +956,17 @@ public class AccessPoint implements Comparable<AccessPoint> {
if (bestResult != null) {
ssid = bestResult.SSID;
bssid = bestResult.BSSID;
- security = getSecurity(bestResult);
- if (security == SECURITY_PSK || security == SECURITY_SAE
- || security == SECURITY_PSK_SAE_TRANSITION) {
+ security = getSecurity(mContext, bestResult);
+ if (security == SECURITY_PSK || security == SECURITY_SAE) {
pskType = getPskType(bestResult);
}
if (security == SECURITY_EAP) {
mEapType = getEapType(bestResult);
}
+
+ mIsPskSaeTransitionMode = AccessPoint.isPskSaeTransitionMode(bestResult);
+ mIsOweTransitionMode = AccessPoint.isOweTransitionMode(bestResult);
+
mIsCarrierAp = bestResult.isCarrierAp;
mCarrierApEapType = bestResult.carrierApEapType;
mCarrierName = bestResult.carrierName;
@@ -886,6 +999,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
return concise ? context.getString(R.string.wifi_security_short_eap) :
context.getString(R.string.wifi_security_eap);
}
+
+ if (mIsPskSaeTransitionMode) {
+ return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
+ context.getString(R.string.wifi_security_psk_sae);
+ }
+
switch(security) {
case SECURITY_EAP:
switch (mEapType) {
@@ -925,20 +1044,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
return concise ? context.getString(R.string.wifi_security_short_wep) :
context.getString(R.string.wifi_security_wep);
case SECURITY_SAE:
- case SECURITY_PSK_SAE_TRANSITION:
- if (pskType == PSK_SAE) {
- return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
- context.getString(R.string.wifi_security_psk_sae);
- } else {
- return concise ? context.getString(R.string.wifi_security_short_sae) :
- context.getString(R.string.wifi_security_sae);
- }
- case SECURITY_OWE_TRANSITION:
- if (mConfig != null && getSecurity(mConfig) == SECURITY_OWE) {
- return concise ? context.getString(R.string.wifi_security_short_owe) :
- context.getString(R.string.wifi_security_owe);
- }
- return concise ? "" : context.getString(R.string.wifi_security_none);
+ return concise ? context.getString(R.string.wifi_security_short_sae) :
+ context.getString(R.string.wifi_security_sae);
case SECURITY_OWE:
return concise ? context.getString(R.string.wifi_security_short_owe) :
context.getString(R.string.wifi_security_owe);
@@ -1250,7 +1357,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
return networkId == info.getNetworkId();
} else if (config != null) {
- return isKeyEqual(getKey(config));
+ return matches(config, info);
} else {
// Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
// (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
@@ -1322,43 +1429,14 @@ public class AccessPoint implements Comparable<AccessPoint> {
savedState.putLong(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS,
mSubscriptionExpirationTimeInMillis);
savedState.putInt(KEY_PASSPOINT_CONFIGURATION_VERSION, mPasspointConfigurationVersion);
+ savedState.putBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE, mIsPskSaeTransitionMode);
+ savedState.putBoolean(KEY_IS_OWE_TRANSITION_MODE, mIsOweTransitionMode);
}
public void setListener(AccessPointListener listener) {
mAccessPointListener = listener;
}
- private static final String sPskSuffix = "," + String.valueOf(SECURITY_PSK);
- private static final String sSaeSuffix = "," + String.valueOf(SECURITY_SAE);
- private static final String sPskSaeSuffix = "," + String.valueOf(SECURITY_PSK_SAE_TRANSITION);
- private static final String sOweSuffix = "," + String.valueOf(SECURITY_OWE);
- private static final String sOpenSuffix = "," + String.valueOf(SECURITY_NONE);
- private static final String sOweTransSuffix = "," + String.valueOf(SECURITY_OWE_TRANSITION);
-
- private boolean isKeyEqual(String compareTo) {
- if (mKey == null) {
- return false;
- }
-
- if (compareTo.endsWith(sPskSuffix) || compareTo.endsWith(sSaeSuffix)) {
- if (mKey.endsWith(sPskSaeSuffix)) {
- // Special handling for PSK-SAE transition mode. If the AP has advertised both,
- // we compare the key with both PSK and SAE for a match.
- return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
- compareTo.substring(0, compareTo.lastIndexOf(',')));
- }
- }
- if (compareTo.endsWith(sOpenSuffix) || compareTo.endsWith(sOweSuffix)) {
- if (mKey.endsWith(sOweTransSuffix)) {
- // Special handling for OWE/Open networks. If AP advertises OWE in transition mode
- // and we have an Open network saved, allow this connection to be established.
- return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
- compareTo.substring(0, compareTo.lastIndexOf(',')));
- }
- }
- return mKey.equals(compareTo);
- }
-
/**
* Sets {@link #mScanResults} to the given collection and updates info based on the best RSSI
* scan result.
@@ -1375,11 +1453,10 @@ public class AccessPoint implements Comparable<AccessPoint> {
// Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
if (mKey != null && !isPasspoint() && !isOsuProvider()) {
for (ScanResult result : scanResults) {
- String scanResultKey = AccessPoint.getKey(result);
- if (!isKeyEqual(scanResultKey)) {
+ if (!matches(result)) {
Log.d(TAG, String.format(
- "ScanResult %s\nkey of %s did not match current AP key %s",
- result, scanResultKey, mKey));
+ "ScanResult %s\nkey of %s did not match current AP key %s",
+ result, getKey(mContext, result), mKey));
return;
}
}
@@ -1653,11 +1730,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
private static int getPskType(ScanResult result) {
boolean wpa = result.capabilities.contains("WPA-PSK");
boolean wpa2 = result.capabilities.contains("RSN-PSK");
- boolean wpa3TransitionMode = result.capabilities.contains("PSK+SAE");
boolean wpa3 = result.capabilities.contains("RSN-SAE");
- if (wpa3TransitionMode) {
- return PSK_SAE;
- } else if (wpa2 && wpa) {
+ if (wpa2 && wpa) {
return PSK_WPA_WPA2;
} else if (wpa2) {
return PSK_WPA2;
@@ -1684,22 +1758,37 @@ public class AccessPoint implements Comparable<AccessPoint> {
return EAP_UNKNOWN;
}
- private static int getSecurity(ScanResult result) {
- if (result.capabilities.contains("WEP")) {
+ private static int getSecurity(Context context, ScanResult result) {
+ final boolean isWep = result.capabilities.contains("WEP");
+ final boolean isSae = result.capabilities.contains("SAE");
+ final boolean isPsk = result.capabilities.contains("PSK");
+ final boolean isEapSuiteB192 = result.capabilities.contains("EAP_SUITE_B_192");
+ final boolean isEap = result.capabilities.contains("EAP");
+ final boolean isOwe = result.capabilities.contains("OWE");
+ final boolean isOweTransition = result.capabilities.contains("OWE_TRANSITION");
+
+ if (isSae && isPsk) {
+ final WifiManager wifiManager = (WifiManager)
+ context.getSystemService(Context.WIFI_SERVICE);
+ return wifiManager.isWpa3SaeSupported() ? SECURITY_SAE : SECURITY_PSK;
+ }
+ if (isOweTransition) {
+ final WifiManager wifiManager = (WifiManager)
+ context.getSystemService(Context.WIFI_SERVICE);
+ return wifiManager.isEnhancedOpenSupported() ? SECURITY_OWE : SECURITY_NONE;
+ }
+
+ if (isWep) {
return SECURITY_WEP;
- } else if (result.capabilities.contains("PSK+SAE")) {
- return SECURITY_PSK_SAE_TRANSITION;
- } else if (result.capabilities.contains("SAE")) {
+ } else if (isSae) {
return SECURITY_SAE;
- } else if (result.capabilities.contains("PSK")) {
+ } else if (isPsk) {
return SECURITY_PSK;
- } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
+ } else if (isEapSuiteB192) {
return SECURITY_EAP_SUITE_B;
- } else if (result.capabilities.contains("EAP")) {
+ } else if (isEap) {
return SECURITY_EAP;
- } else if (result.capabilities.contains("OWE_TRANSITION")) {
- return SECURITY_OWE_TRANSITION;
- } else if (result.capabilities.contains("OWE")) {
+ } else if (isOwe) {
return SECURITY_OWE;
}
return SECURITY_NONE;
@@ -1745,10 +1834,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
return "SUITE_B";
} else if (security == SECURITY_OWE) {
return "OWE";
- } else if (security == SECURITY_PSK_SAE_TRANSITION) {
- return "PSK+SAE";
- } else if (security == SECURITY_OWE_TRANSITION) {
- return "OWE_TRANSITION";
}
return "NONE";
}
@@ -1776,8 +1861,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
* Return true if this is an open network AccessPoint.
*/
public boolean isOpenNetwork() {
- return security == SECURITY_NONE || security == SECURITY_OWE
- || security == SECURITY_OWE_TRANSITION;
+ return security == SECURITY_NONE || security == SECURITY_OWE;
}
/**
@@ -1926,4 +2010,61 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
}
}
+
+ public boolean isPskSaeTransitionMode() {
+ return mIsPskSaeTransitionMode;
+ }
+
+ public boolean isOweTransitionMode() {
+ return mIsOweTransitionMode;
+ }
+
+ private static boolean isPskSaeTransitionMode(ScanResult scanResult) {
+ return scanResult.capabilities.contains("PSK")
+ && scanResult.capabilities.contains("SAE");
+ }
+
+ private static boolean isOweTransitionMode(ScanResult scanResult) {
+ return scanResult.capabilities.contains("OWE_TRANSITION");
+ }
+
+ private boolean isSameSsidOrBssid(ScanResult scanResult) {
+ if (scanResult == null) {
+ return false;
+ }
+
+ if (TextUtils.equals(ssid, scanResult.SSID)) {
+ return true;
+ } else if (scanResult.BSSID != null && TextUtils.equals(bssid, scanResult.BSSID)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isSameSsidOrBssid(WifiInfo wifiInfo) {
+ if (wifiInfo == null) {
+ return false;
+ }
+
+ if (TextUtils.equals(ssid, removeDoubleQuotes(wifiInfo.getSSID()))) {
+ return true;
+ } else if (wifiInfo.getBSSID() != null && TextUtils.equals(bssid, wifiInfo.getBSSID())) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isSameSsidOrBssid(AccessPoint accessPoint) {
+ if (accessPoint == null) {
+ return false;
+ }
+
+ if (TextUtils.equals(ssid, accessPoint.getSsid())) {
+ return true;
+ } else if (accessPoint.getBssid() != null
+ && TextUtils.equals(bssid, accessPoint.getBssid())) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index dae546497aba..6269a717b333 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -201,8 +201,7 @@ public class AccessPointPreference extends Preference {
return;
}
if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
- && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)
- && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
+ && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
mFrictionSld.setState(STATE_SECURED);
} else if (mAccessPoint.isMetered()) {
mFrictionSld.setState(STATE_METERED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 23b16e811606..ba6a8ea31987 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -70,8 +70,10 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
/**
* Tracks saved or available wifi networks and their state.
@@ -475,7 +477,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
continue;
}
- String apKey = AccessPoint.getKey(result);
+ String apKey = AccessPoint.getKey(mContext, result);
List<ScanResult> resultList;
if (scanResultsByApKey.containsKey(apKey)) {
resultList = scanResultsByApKey.get(apKey);
@@ -547,14 +549,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private void updateAccessPoints(final List<ScanResult> newScanResults,
List<WifiConfiguration> configs) {
- // Map configs and scan results necessary to make AccessPoints
- final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
- if (configs != null) {
- for (WifiConfiguration config : configs) {
- configsByKey.put(AccessPoint.getKey(config), config);
- }
- }
-
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs);
@@ -586,7 +580,26 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
getCachedOrCreate(entry.getValue(), cachedAccessPoints);
// Update the matching config if there is one, to populate saved network info
- accessPoint.update(configsByKey.get(entry.getKey()));
+ final List<WifiConfiguration> matchedConfigs = configs.stream()
+ .filter(config -> accessPoint.matches(config))
+ .collect(Collectors.toList());
+
+ final int matchedConfigCount = matchedConfigs.size();
+ if (matchedConfigCount == 0) {
+ accessPoint.update(null);
+ } else if (matchedConfigCount == 1) {
+ accessPoint.update(matchedConfigs.get(0));
+ } else {
+ // We may have 2 matched configured WifiCongiguration if the AccessPoint is
+ // of PSK/SAE transition mode or open/OWE transition mode.
+ Optional<WifiConfiguration> preferredConfig = matchedConfigs.stream()
+ .filter(config -> isSaeOrOwe(config)).findFirst();
+ if (preferredConfig.isPresent()) {
+ accessPoint.update(preferredConfig.get());
+ } else {
+ accessPoint.update(matchedConfigs.get(0));
+ }
+ }
accessPoints.add(accessPoint);
}
@@ -652,6 +665,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
conditionallyNotifyListeners();
}
+ private static boolean isSaeOrOwe(WifiConfiguration config) {
+ final int security = AccessPoint.getSecurity(config);
+ return security == AccessPoint.SECURITY_SAE || security == AccessPoint.SECURITY_OWE;
+ }
+
@VisibleForTesting
List<AccessPoint> updatePasspointAccessPoints(
List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans,
@@ -700,7 +718,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private AccessPoint getCachedOrCreate(
List<ScanResult> scanResults,
List<AccessPoint> cache) {
- AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0)));
+ AccessPoint accessPoint = getCachedByKey(cache,
+ AccessPoint.getKey(mContext, scanResults.get(0)));
if (accessPoint == null) {
accessPoint = new AccessPoint(mContext, scanResults);
} else {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 7b1c3825fcc6..325366ea6e40 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -39,6 +39,7 @@ import android.net.ScoredNetwork;
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -1273,7 +1274,7 @@ public class AccessPointTest {
@Test
public void testGetKey_matchesKeysCorrectly() {
AccessPoint ap = new AccessPoint(mContext, mScanResults);
- assertThat(ap.getKey()).isEqualTo(AccessPoint.getKey(mScanResults.get(0)));
+ assertThat(ap.getKey()).isEqualTo(AccessPoint.getKey(mContext, mScanResults.get(0)));
WifiConfiguration spyConfig = spy(new WifiConfiguration());
when(spyConfig.isPasspoint()).thenReturn(true);
@@ -1295,6 +1296,44 @@ public class AccessPointTest {
}
/**
+ * Test that getKey returns a key of SAE type for a PSK/SAE transition mode ScanResult.
+ */
+ @Test
+ public void testGetKey_supportSaeTransitionMode_shouldGetSaeKey() {
+ ScanResult scanResult = createScanResult(TEST_SSID, TEST_BSSID, DEFAULT_RSSI);
+ scanResult.capabilities =
+ "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+ StringBuilder key = new StringBuilder();
+ key.append(AccessPoint.KEY_PREFIX_AP);
+ key.append(TEST_SSID);
+ key.append(',');
+ key.append(AccessPoint.SECURITY_SAE);
+
+ assertThat(AccessPoint.getKey(mMockContext, scanResult)).isEqualTo(key.toString());
+ }
+
+ /**
+ * Test that getKey returns a key of PSK type for a PSK/SAE transition mode ScanResult.
+ */
+ @Test
+ public void testGetKey_notSupportSaeTransitionMode_shouldGetPskKey() {
+ ScanResult scanResult = createScanResult(TEST_SSID, TEST_BSSID, DEFAULT_RSSI);
+ scanResult.capabilities =
+ "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+ StringBuilder key = new StringBuilder();
+ key.append(AccessPoint.KEY_PREFIX_AP);
+ key.append(TEST_SSID);
+ key.append(',');
+ key.append(AccessPoint.SECURITY_PSK);
+
+ assertThat(AccessPoint.getKey(mMockContext, scanResult)).isEqualTo(key.toString());
+ }
+
+ /**
* Verifies that the Passpoint AccessPoint constructor creates AccessPoints whose isPasspoint()
* returns true.
*/
@@ -1537,12 +1576,120 @@ public class AccessPointTest {
bundle.putInt("key_security", i);
ap = new AccessPoint(InstrumentationRegistry.getTargetContext(), bundle);
- if (i == AccessPoint.SECURITY_NONE || i == AccessPoint.SECURITY_OWE
- || i == AccessPoint.SECURITY_OWE_TRANSITION) {
+ if (i == AccessPoint.SECURITY_NONE || i == AccessPoint.SECURITY_OWE) {
assertThat(ap.isOpenNetwork()).isTrue();
} else {
assertThat(ap.isOpenNetwork()).isFalse();
}
}
}
+
+ /**
+ * Verifies that matches(AccessPoint other) matches a PSK/SAE transition mode AP to a PSK or a
+ * SAE AP.
+ */
+ @Test
+ public void testMatches1_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() {
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+ AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+ // Transition mode AP matches a SAE AP.
+ AccessPoint saeAccessPoint = new TestAccessPointBuilder(mContext)
+ .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID))
+ .setSecurity(AccessPoint.SECURITY_SAE)
+ .build();
+ assertThat(pskSaeTransitionModeAp.matches(saeAccessPoint)).isTrue();
+
+ // Transition mode AP matches a PSK AP.
+ AccessPoint pskAccessPoint = new TestAccessPointBuilder(mContext)
+ .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID))
+ .setSecurity(AccessPoint.SECURITY_PSK)
+ .build();
+
+ assertThat(pskSaeTransitionModeAp.matches(pskAccessPoint)).isTrue();
+
+ // Transition mode AP does not match a SAE AP if the device does not support SAE.
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+ pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+ saeAccessPoint = new TestAccessPointBuilder(mContext)
+ .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID))
+ .setSecurity(AccessPoint.SECURITY_SAE)
+ .build();
+
+ assertThat(pskSaeTransitionModeAp.matches(saeAccessPoint)).isFalse();
+ }
+
+ /**
+ * Verifies that matches(WifiConfiguration config) matches a PSK/SAE transition mode AP to a PSK
+ * or a SAE WifiConfiguration.
+ */
+ @Test
+ public void testMatches2_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() {
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+ AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+ // Transition mode AP matches a SAE WifiConfiguration.
+ WifiConfiguration saeConfig = new WifiConfiguration();
+ saeConfig.SSID = TEST_SSID;
+ saeConfig.allowedKeyManagement.set(KeyMgmt.SAE);
+
+ assertThat(pskSaeTransitionModeAp.matches(saeConfig)).isTrue();
+
+ // Transition mode AP matches a PSK WifiConfiguration.
+ WifiConfiguration pskConfig = new WifiConfiguration();
+ pskConfig.SSID = TEST_SSID;
+ pskConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+
+ assertThat(pskSaeTransitionModeAp.matches(pskConfig)).isTrue();
+
+ // Transition mode AP does not matches a SAE WifiConfiguration if the device does not
+ // support SAE.
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+ pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+ assertThat(pskSaeTransitionModeAp.matches(saeConfig)).isFalse();
+ }
+
+ /**
+ * Verifies that matches(ScanResult scanResult) matches a PSK/SAE transition mode AP to a PSK
+ * or a SAE ScanResult.
+ */
+ @Test
+ public void testMatches3_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() {
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+ AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+ // Transition mode AP matches a SAE ScanResult.
+ ScanResult saeScanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+ TEST_BSSID, DEFAULT_RSSI);
+ saeScanResult.capabilities = "[SAE-CCMP][ESS][WPS]";
+
+ assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isTrue();
+
+ // Transition mode AP matches a PSK ScanResult.
+ ScanResult pskScanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+ TEST_BSSID, DEFAULT_RSSI);
+ pskScanResult.capabilities = "[RSN-PSK-CCMP][ESS][WPS]";
+
+ assertThat(pskSaeTransitionModeAp.matches(pskScanResult)).isTrue();
+
+ // Transition mode AP does not matches a SAE ScanResult if the device does not support SAE.
+ when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+ pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+ assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isFalse();
+ }
+
+ private AccessPoint getPskSaeTransitionModeAp() {
+ ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+ TEST_BSSID, DEFAULT_RSSI);
+ scanResult.capabilities =
+ "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+ return new TestAccessPointBuilder(mMockContext)
+ .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult)))
+ .build();
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4731e6894baf..086b20facb46 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -62,6 +62,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -766,6 +768,23 @@ final class SettingsState {
}
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
+ if (t instanceof IOException) {
+ // we failed to create a directory, so log the permissions and existence
+ // state for the settings file and directory
+ logSettingsDirectoryInformation(destination.getBaseFile());
+ if (t.getMessage().contains("Couldn't create directory")) {
+ // attempt to create the directory with Files.createDirectories, which
+ // throws more informative errors than File.mkdirs.
+ Path parentPath = destination.getBaseFile().getParentFile().toPath();
+ try {
+ Files.createDirectories(parentPath);
+ Slog.i(LOG_TAG, "Successfully created " + parentPath);
+ } catch (Throwable t2) {
+ Slog.e(LOG_TAG, "Failed to write " + parentPath
+ + " with Files.writeDirectories", t2);
+ }
+ }
+ }
destination.failWrite(out);
} finally {
IoUtils.closeQuietly(out);
@@ -779,6 +798,33 @@ final class SettingsState {
}
}
+ private static void logSettingsDirectoryInformation(File settingsFile) {
+ File parent = settingsFile.getParentFile();
+ Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile
+ + " with stacktrace ", new Exception());
+ File ancestorDir = parent;
+ while (ancestorDir != null) {
+ if (!ancestorDir.exists()) {
+ Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+ + " does not exist");
+ ancestorDir = ancestorDir.getParentFile();
+ } else {
+ Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+ + " exists");
+ Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+ + " permissions: r: " + ancestorDir.canRead() + " w: "
+ + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute());
+ File ancestorParent = ancestorDir.getParentFile();
+ if (ancestorParent != null) {
+ Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent
+ + " permissions: r: " + ancestorParent.canRead() + " w: "
+ + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute());
+ }
+ break;
+ }
+ }
+ }
+
static void writeSingleSetting(int version, XmlSerializer serializer, String id,
String name, String value, String defaultValue, String packageName,
String tag, boolean defaultSysSet) throws IOException {
@@ -853,6 +899,7 @@ final class SettingsState {
in = new AtomicFile(mStatePersistFile).openRead();
} catch (FileNotFoundException fnfe) {
Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+ logSettingsDirectoryInformation(mStatePersistFile);
addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
return;
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6f6803817138..35147049e43e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -325,6 +325,14 @@
android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
+ <activity
+ android:name=".bubbles.BubbleOverflowActivity"
+ android:theme="@style/BubbleOverflow"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="always"
+ android:resizeableActivity="true">
+ </activity>
+
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/tuner"
diff --git a/packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.png b/packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.png
deleted file mode 100644
index 135dabb63069..000000000000
--- a/packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/tv_bg_item_app_info.xml b/packages/SystemUI/res/drawable/tv_circle_dark.xml
index 1bbb8c3e8ef0..d1ba8e71ec31 100644
--- a/packages/SystemUI/res/drawable/tv_bg_item_app_info.xml
+++ b/packages/SystemUI/res/drawable/tv_circle_dark.xml
@@ -16,7 +16,9 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners android:radius="24dp"/>
- <solid android:color="@color/tv_audio_recording_bar_chip_background"/>
-</shape>
+ android:shape="oval">
+
+ <solid
+ android:color="@color/tv_audio_recording_indicator_background" />
+
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/circle_red.xml b/packages/SystemUI/res/drawable/tv_circle_white_translucent.xml
index fd3c125e5ab8..55d21de00ca3 100644
--- a/packages/SystemUI/res/drawable/circle_red.xml
+++ b/packages/SystemUI/res/drawable/tv_circle_white_translucent.xml
@@ -16,6 +16,9 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
- <solid android:color="@color/red"/>
+ android:shape="oval">
+
+ <solid
+ android:color="@color/tv_audio_recording_indicator_pulse" />
+
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_ic_mic_white.xml b/packages/SystemUI/res/drawable/tv_ic_mic_white.xml
index 1bea8a19c8b9..d887113c7717 100644
--- a/packages/SystemUI/res/drawable/tv_ic_mic_white.xml
+++ b/packages/SystemUI/res/drawable/tv_ic_mic_white.xml
@@ -1,25 +1,27 @@
-<?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
- -->
+Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportWidth="44"
- android:viewportHeight="44"
- android:width="44dp"
- android:height="44dp">
- <path
- android:pathData="M22 25.6666667C25.0433333 25.6666667 27.4816667 23.21 27.4816667 20.1666667L27.5 9.16666667C27.5 6.12333333 25.0433333 3.66666667 22 3.66666667C18.9566667 3.66666667 16.5 6.12333333 16.5 9.16666667L16.5 20.1666667C16.5 23.21 18.9566667 25.6666667 22 25.6666667ZM31.7166667 20.1666667C31.7166667 25.6666667 27.06 29.5166667 22 29.5166667C16.94 29.5166667 12.2833333 25.6666667 12.2833333 20.1666667L9.16666667 20.1666667C9.16666667 26.4183333 14.1533333 31.5883333 20.1666667 32.4866667L20.1666667 38.5L23.8333333 38.5L23.8333333 32.4866667C29.8466667 31.6066667 34.8333333 26.4366667 34.8333333 20.1666667L31.7166667 20.1666667Z"
- android:fillColor="@android:color/white" />
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/>
</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
new file mode 100644
index 000000000000..9b48a70d9439
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <corners
+ android:bottomLeftRadius="8dp"
+ android:topLeftRadius="8dp" />
+ <solid android:color="@color/tv_audio_recording_indicator_background" />
+
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
new file mode 100644
index 000000000000..03348756231b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <corners
+ android:bottomRightRadius="8dp"
+ android:topRightRadius="8dp" />
+ <solid android:color="@color/tv_audio_recording_indicator_background" />
+
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_gradient_protection.xml b/packages/SystemUI/res/drawable/tv_ring_white.xml
index ee5cbc7e6ba0..0f7cc1082f71 100644
--- a/packages/SystemUI/res/drawable/tv_gradient_protection.xml
+++ b/packages/SystemUI/res/drawable/tv_ring_white.xml
@@ -15,8 +15,11 @@
~ limitations under the License.
-->
-<!-- gradient protection for cards -->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/tv_card_gradient_protection"
- android:tileMode="repeat"
-/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <stroke
+ android:width="1dp"
+ android:color="@android:color/white" />
+
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
new file mode 100644
index 000000000000..4cee74615bd2
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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
+ -->
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bubble_overflow_recycler"
+ android:scrollbars="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
new file mode 100644
index 000000000000..f04226e7ceaf
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="32dp">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/icon_texts_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <FrameLayout
+ android:layout_width="90dp"
+ android:layout_height="94dp">
+
+ <View
+ android:id="@+id/icon_container_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/tv_rect_dark_left_rounded"/>
+
+ <FrameLayout
+ android:id="@+id/icon_mic"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginBottom="12dp">
+
+ <View
+ android:layout_width="54dp"
+ android:layout_height="54dp"
+ android:layout_gravity="center"
+ android:background="@drawable/tv_circle_dark"/>
+
+ <ImageView
+ android:id="@+id/pulsating_circle"
+ android:layout_width="54dp"
+ android:layout_height="54dp"
+ android:layout_gravity="center"
+ android:background="@drawable/tv_circle_white_translucent"/>
+
+ <ImageView
+ android:layout_width="54dp"
+ android:layout_height="54dp"
+ android:layout_gravity="center"
+ android:src="@drawable/tv_ring_white"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_gravity="center"
+ android:background="@drawable/tv_ic_mic_white"/>
+ </FrameLayout>
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/texts_container"
+ android:layout_width="wrap_content"
+ android:layout_height="94dp"
+ android:background="@color/tv_audio_recording_indicator_background"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:visibility="visible">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/mic_active"
+ android:textColor="@android:color/white"
+ android:fontFamily="sans-serif"
+ android:textSize="20dp"/>
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:text="SomeApplication accessed your microphone"
+ android:textColor="@android:color/white"
+ android:fontFamily="sans-serif"
+ android:textSize="16dp"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <View
+ android:id="@+id/bg_right"
+ android:layout_width="24dp"
+ android:layout_height="94dp"
+ android:background="@drawable/tv_rect_dark_right_rounded"
+ android:visibility="visible"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_item_app_info.xml b/packages/SystemUI/res/layout/tv_item_app_info.xml
deleted file mode 100644
index b40589ec80c6..000000000000
--- a/packages/SystemUI/res/layout/tv_item_app_info.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginLeft="8dp"
- android:paddingHorizontal="12dp"
- android:gravity="center_vertical"
- android:background="@drawable/tv_bg_item_app_info">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginRight="8dp"/>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/tv_audio_recording_bar_text"
- android:fontFamily="sans-serif"
- android:textSize="14sp"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml b/packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml
deleted file mode 100644
index b9dffbb4de20..000000000000
--- a/packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:orientation="vertical">
-
- <!-- Gradient Protector -->
- <View
- android:layout_width="match_parent"
- android:layout_height="102.5dp"
- android:background="@drawable/tv_gradient_protection"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="72dp"
- android:background="@color/tv_audio_recording_bar_background"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginLeft="42dp"
- android:layout_marginVertical="12dp"
- android:padding="8dp"
- android:background="@drawable/circle_red"
- android:scaleType="centerInside"
- android:src="@drawable/tv_ic_mic_white"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="24dp"
- android:text="Audio recording by"
- android:textColor="@color/tv_audio_recording_bar_text"
- android:fontFamily="sans-serif"
- android:textSize="14sp"/>
-
- <LinearLayout
- android:id="@+id/container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6becd21984b9..79629e4c6d7e 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -154,12 +154,5 @@
<declare-styleable name="CaptionsToggleImageButton">
<attr name="optedOut" format="boolean" />
</declare-styleable>
-
- <!-- Theme attributes used to style the appearance of expanded Bubbles -->
- <declare-styleable name="BubbleExpandedView">
- <attr name="android:colorBackgroundFloating" />
- <attr name="android:dialogCornerRadius" />
- </declare-styleable>
-
</resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index db225428a348..53cd9716c98e 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -22,13 +22,9 @@
<color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
<color name="recents_tv_text_shadow_color">#7F000000</color>
-
- <!-- Text color used in audio recording bar: G50 -->
- <color name="tv_audio_recording_bar_text">#FFF8F9FA</color>
- <!-- Background color for a chip in audio recording bar: G800 -->
- <color name="tv_audio_recording_bar_chip_background">#FF3C4043</color>
- <!-- Audio recording bar background color: G900 -->
- <color name="tv_audio_recording_bar_background">#FF202124</color>
+ <!-- Background color for audio recording indicator (G800) -->
+ <color name="tv_audio_recording_indicator_background">#FF3C4043</color>
+ <color name="tv_audio_recording_indicator_pulse">#4DFFFFFF</color>
<color name="red">#FFCC0000</color>
</resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index a9bdb71de039..6d61ff989cb1 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -31,4 +31,8 @@
<string name="pip_close">Close PIP</string>
<!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
<string name="pip_fullscreen">Full screen</string>
+
+ <!-- Title and subtitle for AudioRecordingIndicator -->
+ <string name="mic_active">Microphone Active</string>
+ <string name="app_accessed_mic">%1$s accessed your microphone</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 926d01685075..3ff824374aac 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -15,6 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="BubbleOverflow" parent="@android:style/Theme.NoTitleBar"></style>
<style name="ClearAllButtonDefaultMargins">
<item name="android:layout_marginStart">0dp</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
index eaf8d9b57398..35952f55ec1e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import android.app.ActivityManager;
+import android.graphics.Bitmap;
public class TaskDescriptionCompat {
@@ -37,4 +38,12 @@ public class TaskDescriptionCompat {
? mTaskDescription.getBackgroundColor()
: 0;
}
+
+ public static Bitmap getIcon(ActivityManager.TaskDescription desc, int userId) {
+ if (desc.getInMemoryIcon() != null) {
+ return desc.getInMemoryIcon();
+ }
+ return ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+ desc.getIconFilename(), userId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 72a403035294..4749addc7139 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -280,6 +280,7 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman
R.layout.size_compat_mode_hint, null /* root */);
PopupWindow popupWindow = new PopupWindow(popupView,
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ popupWindow.setWindowLayoutType(mWinParams.type);
popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
popupWindow.setClippingEnabled(false);
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index adb288a9ce82..5cc70bc27c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -46,7 +46,7 @@ private const val MSG_ADD_RECEIVER = 0
private const val MSG_REMOVE_RECEIVER = 1
private const val MSG_REMOVE_RECEIVER_FOR_USER = 2
private const val TAG = "BroadcastDispatcher"
-private const val DEBUG = false
+private const val DEBUG = true
/**
* SystemUI master Broadcast Dispatcher.
@@ -147,7 +147,13 @@ open class BroadcastDispatcher @Inject constructor (
when (msg.what) {
MSG_ADD_RECEIVER -> {
val data = msg.obj as ReceiverData
- val userId = data.user.identifier
+ // If the receiver asked to be registered under the current user, we register
+ // under the actual current user.
+ val userId = if (data.user.identifier == UserHandle.USER_CURRENT) {
+ context.userId
+ } else {
+ data.user.identifier
+ }
if (userId < UserHandle.USER_ALL) {
if (DEBUG) Log.w(TAG, "Register receiver for invalid user: $userId")
return
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index f3a7ca9c0a75..7934e10c8605 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -297,7 +297,9 @@ class Bubble {
* Whether the bubble for this notification should show a dot indicating updated content.
*/
boolean showDot() {
- return mShowBubbleUpdateDot && !mEntry.shouldSuppressNotificationDot();
+ return mShowBubbleUpdateDot
+ && !mEntry.shouldSuppressNotificationDot()
+ && !shouldSuppressNotification();
}
/**
@@ -305,6 +307,7 @@ class Bubble {
*/
boolean showFlyout() {
return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
+ && !shouldSuppressNotification()
&& !mEntry.shouldSuppressNotificationList();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 19381940543e..b5622432f919 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -878,12 +878,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (DEBUG_BUBBLE_CONTROLLER) {
Log.d(TAG, "[BubbleData]");
- Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getBubbles(),
mBubbleData.getSelectedBubble()));
if (mStackView != null) {
Log.d(TAG, "[BubbleStackView]");
- Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(mStackView.getBubblesOnScreen(),
mStackView.getExpandedBubble()));
}
}
@@ -972,23 +972,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
pw.println();
}
- static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
- StringBuilder sb = new StringBuilder();
- for (Bubble bubble : bubbles) {
- if (bubble == null) {
- sb.append(" <null> !!!!!\n");
- } else {
- boolean isSelected = (bubble == selected);
- sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
- ((isSelected) ? "->" : " "),
- bubble.getLastActivity(),
- (bubble.isOngoing() ? 1 : 0),
- bubble.getKey()));
- }
- }
- return sb.toString();
- }
-
/**
* This task stack listener is responsible for responding to tasks moved to the front
* which are on the default (main) display. When this happens, expanded bubbles must be
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 034bff345d71..b7df5baa4cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -48,6 +48,7 @@ import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Singleton;
+import com.android.systemui.R;
/**
* Keeps track of active bubbles.
@@ -57,8 +58,6 @@ public class BubbleData {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES;
- private static final int MAX_BUBBLES = 5;
-
private static final Comparator<Bubble> BUBBLES_BY_SORT_KEY_DESCENDING =
Comparator.comparing(BubbleData::sortKey).reversed();
@@ -115,6 +114,7 @@ public class BubbleData {
private final List<Bubble> mBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
+ private final int mMaxBubbles;
// State tracked during an operation -- keeps track of what listener events to dispatch.
private Update mStateChange;
@@ -144,6 +144,7 @@ public class BubbleData {
mContext = context;
mBubbles = new ArrayList<>();
mStateChange = new Update(mBubbles);
+ mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
}
public boolean hasBubbles() {
@@ -329,7 +330,7 @@ public class BubbleData {
}
private void trim() {
- if (mBubbles.size() > MAX_BUBBLES) {
+ if (mBubbles.size() > mMaxBubbles) {
mBubbles.stream()
// sort oldest first (ascending lastActivity)
.sorted(Comparator.comparingLong(Bubble::getLastActivity))
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
index a912eccd4d5e..319066221600 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
+import java.util.List;
+
/**
* Common class for the various debug {@link android.util.Log} output configuration in the Bubbles
* package.
@@ -38,5 +40,23 @@ public class BubbleDebugConfig {
static final boolean DEBUG_BUBBLE_STACK_VIEW = false;
static final boolean DEBUG_BUBBLE_EXPANDED_VIEW = false;
static final boolean DEBUG_EXPERIMENTS = true;
+ static final boolean DEBUG_OVERFLOW = false;
+ static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
+ StringBuilder sb = new StringBuilder();
+ for (Bubble bubble : bubbles) {
+ if (bubble == null) {
+ sb.append(" <null> !!!!!\n");
+ } else {
+ boolean isSelected = (selected != null && bubble == selected);
+ String arrow = isSelected ? "=>" : " ";
+ sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
+ arrow,
+ bubble.getLastActivity(),
+ (bubble.isOngoing() ? 1 : 0),
+ bubble.getKey()));
+ }
+ }
+ return sb.toString();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index efc955d9bca4..63d036d6362d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -275,17 +275,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
void applyThemeAttrs() {
- TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
- int bgColor = ta.getColor(
- R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
- float cornerRadius = ta.getDimension(
- R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {
+ android.R.attr.colorBackgroundFloating,
+ android.R.attr.dialogCornerRadius});
+ int bgColor = ta.getColor(0, Color.WHITE);
+ float cornerRadius = ta.getDimensionPixelSize(1, 0);
ta.recycle();
- // Update triangle color.
mPointerDrawable.setTint(bgColor);
-
- // Update ActivityView cornerRadius
if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
mActivityView.setCornerRadius(cornerRadius);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
new file mode 100644
index 000000000000..018b6318575a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -0,0 +1,69 @@
+package com.android.systemui.bubbles;
+
+import android.app.Activity;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.Bundle;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.R;
+
+/**
+ * Activity for showing aged out bubbles.
+ * Must be public to be accessible to androidx...AppComponentFactory
+ */
+public class BubbleOverflowActivity extends Activity {
+ private RecyclerView mRecyclerView;
+ private int mMaxBubbles;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.bubble_overflow_activity);
+ setBackgroundColor();
+
+ mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
+ mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
+ mRecyclerView.setLayoutManager(
+ new GridLayoutManager(getApplicationContext(), /* numberOfColumns */ mMaxBubbles));
+ }
+
+ void setBackgroundColor() {
+ final TypedArray ta = getApplicationContext().obtainStyledAttributes(
+ new int[] {android.R.attr.colorBackgroundFloating});
+ int bgColor = ta.getColor(0, Color.WHITE);
+ ta.recycle();
+ findViewById(android.R.id.content).setBackgroundColor(bgColor);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ public void onRestart() {
+ super.onRestart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ }
+
+ public void onDestroy() {
+ super.onStop();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
index f094cb909cf2..0f71d22b218f 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -47,7 +47,8 @@ public class SysuiLog implements Dumpable {
private final Object mDataLock = new Object();
private final String mId;
private final int mMaxLogs;
- private boolean mEnabled;
+ protected boolean mEnabled;
+ protected boolean mLogToLogcatEnabled;
@VisibleForTesting protected ArrayDeque<Event> mTimeline;
@@ -62,14 +63,18 @@ public class SysuiLog implements Dumpable {
*/
public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) {
this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs,
- SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED));
+ SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED),
+ SystemProperties.getBoolean(SYSPROP_LOGCAT_ENABLED_PREFIX + id,
+ DEFAULT_LOGCAT_ENABLED));
}
@VisibleForTesting
- protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled) {
+ protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled,
+ boolean logcatEnabled) {
mId = id;
mMaxLogs = maxLogs;
mEnabled = enabled;
+ mLogToLogcatEnabled = logcatEnabled;
mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null;
dumpController.registerDumpable(mId, this);
}
@@ -96,7 +101,7 @@ public class SysuiLog implements Dumpable {
mTimeline.add(event);
}
- if (LOG_TO_LOGCAT_ENABLED) {
+ if (mLogToLogcatEnabled) {
final String strEvent = eventToString(event);
switch (event.getLogLevel()) {
case Event.VERBOSE:
@@ -162,9 +167,10 @@ public class SysuiLog implements Dumpable {
}
private static boolean sDebuggable = Build.IS_DEBUGGABLE;
- private static final String SYSPROP_ENABLED_PREFIX = "sysui.log.enabled.";
- private static final boolean LOG_TO_LOGCAT_ENABLED = sDebuggable;
+ private static final String SYSPROP_ENABLED_PREFIX = "persist.sysui.log.enabled.";
+ private static final String SYSPROP_LOGCAT_ENABLED_PREFIX = "persist.sysui.log.enabled.logcat.";
private static final boolean DEFAULT_ENABLED = sDebuggable;
+ private static final boolean DEFAULT_LOGCAT_ENABLED = false;
private static final int DEFAULT_MAX_DEBUG_LOGS = 100;
private static final int DEFAULT_MAX_LOGS = 50;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 5bb882e2355f..d40e25064352 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -250,6 +250,14 @@ public class QuickQSMediaPlayer {
return (state.getState() == PlaybackState.STATE_PLAYING);
}
+ /**
+ * Check whether this player has an attached media session.
+ * @return whether there is a controller with a current media session.
+ */
+ public boolean hasMediaSession() {
+ return mController != null && mController.getPlaybackState() != null;
+ }
+
private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index d377f1c793a9..feb10a2e9a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -57,6 +57,12 @@ public class QuickQSPanel extends QSPanel {
private int mMaxTiles;
protected QSPanel mFullPanel;
private QuickQSMediaPlayer mMediaPlayer;
+ private boolean mUsingMediaPlayer;
+ private LinearLayout mHorizontalLinearLayout;
+
+ // Only used with media
+ private QSTileLayout mMediaTileLayout;
+ private QSTileLayout mRegularTileLayout;
@Inject
public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -72,8 +78,9 @@ public class QuickQSPanel extends QSPanel {
removeView((View) mTileLayout);
}
- if (Utils.useQsMediaPlayer(context)) {
- LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext);
+ mUsingMediaPlayer = Utils.useQsMediaPlayer(context);
+ if (mUsingMediaPlayer) {
+ mHorizontalLinearLayout = new LinearLayout(mContext);
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
mHorizontalLinearLayout.setClipChildren(false);
mHorizontalLinearLayout.setClipToPadding(false);
@@ -81,6 +88,8 @@ public class QuickQSPanel extends QSPanel {
LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
mTileLayout = new DoubleLineTileLayout(context);
+ mMediaTileLayout = mTileLayout;
+ mRegularTileLayout = new HeaderTileLayout(context);
lp.setMarginEnd(10);
lp.setMarginStart(0);
mHorizontalLinearLayout.addView((View) mTileLayout, lp);
@@ -95,6 +104,8 @@ public class QuickQSPanel extends QSPanel {
mTileLayout.setListening(mListening);
addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */);
+ ((View) mRegularTileLayout).setVisibility(View.GONE);
+ addView((View) mRegularTileLayout, 0);
super.setPadding(0, 0, 0, 0);
} else {
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -130,6 +141,8 @@ public class QuickQSPanel extends QSPanel {
Dependency.get(TunerService.class).removeTunable(mNumTiles);
}
+
+
@Override
protected String getDumpableTag() {
return TAG;
@@ -152,6 +165,42 @@ public class QuickQSPanel extends QSPanel {
super.drawTile(r, state);
}
+ boolean switchTileLayout() {
+ if (!mUsingMediaPlayer) return false;
+ if (mMediaPlayer.hasMediaSession()
+ && mHorizontalLinearLayout.getVisibility() == View.GONE) {
+ mHorizontalLinearLayout.setVisibility(View.VISIBLE);
+ ((View) mRegularTileLayout).setVisibility(View.GONE);
+ mTileLayout.setListening(false);
+ for (TileRecord record : mRecords) {
+ mTileLayout.removeTile(record);
+ record.tile.removeCallback(record.callback);
+ }
+ mTileLayout = mMediaTileLayout;
+ setTiles(mHost.getTiles());
+ mTileLayout.setListening(mListening);
+ return true;
+ } else if (!mMediaPlayer.hasMediaSession()
+ && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) {
+ mHorizontalLinearLayout.setVisibility(View.GONE);
+ ((View) mRegularTileLayout).setVisibility(View.VISIBLE);
+ mTileLayout.setListening(false);
+ for (TileRecord record : mRecords) {
+ mTileLayout.removeTile(record);
+ record.tile.removeCallback(record.callback);
+ }
+ mTileLayout = mRegularTileLayout;
+ setTiles(mHost.getTiles());
+ mTileLayout.setListening(mListening);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean hasMediaPlayerSession() {
+ return mMediaPlayer.hasMediaSession();
+ }
+
@Override
public void setHost(QSTileHost host, QSCustomizer customizer) {
super.setHost(host, customizer);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index e5cec878b63d..d4af1548af41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -339,7 +339,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
if (mQsDisabled) {
lp.height = resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- } else if (useQsMediaPlayer(mContext)) {
+ } else if (useQsMediaPlayer(mContext) && mHeaderQsPanel.hasMediaPlayerSession()) {
lp.height = Math.max(getMinimumHeight(),
resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_total_height_with_media));
@@ -405,6 +405,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mHeaderTextContainerView.setVisibility(INVISIBLE);
}
}
+ if (expansionFraction < 1 && expansionFraction > 0.99) {
+ if (mHeaderQsPanel.switchTileLayout()) {
+ updateResources();
+ }
+ }
}
public void disable(int state1, int state2, boolean animate) {
@@ -453,6 +458,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
return;
}
mHeaderQsPanel.setListening(listening);
+ if (mHeaderQsPanel.switchTileLayout()) {
+ updateResources();
+ }
mListening = listening;
if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 8ecf2b8421f0..c8b34f1f5b27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -26,6 +26,8 @@ import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
* app ops icon, etc) are handled elsewhere.
@@ -33,11 +35,11 @@ import com.android.systemui.statusbar.phone.StatusBar;
public final class NotificationClicker implements View.OnClickListener {
private static final String TAG = "NotificationClicker";
- private final StatusBar mStatusBar;
+ private final Optional<StatusBar> mStatusBar;
private final BubbleController mBubbleController;
private final NotificationActivityStarter mNotificationActivityStarter;
- public NotificationClicker(StatusBar statusBar,
+ public NotificationClicker(Optional<StatusBar> statusBar,
BubbleController bubbleController,
NotificationActivityStarter notificationActivityStarter) {
mStatusBar = statusBar;
@@ -52,7 +54,8 @@ public final class NotificationClicker implements View.OnClickListener {
return;
}
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK");
+ mStatusBar.ifPresent(statusBar -> statusBar.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"));
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
final StatusBarNotification sbn = row.getEntry().getSbn();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 709143d7275a..e31ad9fd4262 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1264,7 +1264,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mRemoteInputUriController.attach(mEntryManager);
rowBinder.setNotificationClicker(new NotificationClicker(
- this, mBubbleController, mNotificationActivityStarter));
+ Optional.of(this), mBubbleController, mNotificationActivityStarter));
mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
mNotificationListController.bind();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
index 9b685f0ad0f6..74739e19ede9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -16,32 +16,44 @@
package com.android.systemui.statusbar.tv;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.IntDef;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
-import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.R;
-import java.util.ArrayList;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
-import java.util.List;
-
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * A component of {@link TvStatusBar} responsible for notifying the user whenever an application is
+ * recording audio.
+ *
+ * @see TvStatusBar
+ */
class AudioRecordingDisclosureBar {
private static final String TAG = "AudioRecordingDisclosureBar";
private static final boolean DEBUG = false;
@@ -50,121 +62,310 @@ class AudioRecordingDisclosureBar {
// CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
- private static final int ANIM_DURATION_MS = 150;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"STATE_"}, value = {
+ STATE_NOT_SHOWN,
+ STATE_APPEARING,
+ STATE_SHOWN,
+ STATE_MINIMIZING,
+ STATE_MINIMIZED,
+ STATE_MAXIMIZING,
+ STATE_DISAPPEARING
+ })
+ public @interface State {}
+
+ private static final int STATE_NOT_SHOWN = 0;
+ private static final int STATE_APPEARING = 1;
+ private static final int STATE_SHOWN = 2;
+ private static final int STATE_MINIMIZING = 3;
+ private static final int STATE_MINIMIZED = 4;
+ private static final int STATE_MAXIMIZING = 5;
+ private static final int STATE_DISAPPEARING = 6;
+
+ private static final int ANIMATION_DURATION = 600;
+ private static final int MAXIMIZED_DURATION = 3000;
+ private static final int PULSE_BIT_DURATION = 1000;
+ private static final float PULSE_SCALE = 1.25f;
private final Context mContext;
- private final List<String> mAudioRecordingApps = new ArrayList<>();
- private View mView;
- private ViewGroup mAppsInfoContainer;
+
+ private View mIndicatorView;
+ private View mIconTextsContainer;
+ private View mIconContainerBg;
+ private View mIcon;
+ private View mBgRight;
+ private View mTextsContainers;
+ private TextView mTextView;
+
+ @State private int mState = STATE_NOT_SHOWN;
+ private final Set<String> mAudioRecordingApps = new HashSet<>();
+ private final Queue<String> mPendingNotifications = new LinkedList<>();
AudioRecordingDisclosureBar(Context context) {
mContext = context;
}
void start() {
- // Inflate and add audio recording disclosure bar
- createView();
-
// Register AppOpsManager callback
final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(
Context.APP_OPS_SERVICE);
appOpsManager.startWatchingActive(
- new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, mContext.getMainExecutor(),
+ new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
+ mContext.getMainExecutor(),
new OnActiveRecordingListener());
}
- private void createView() {
- //TODO(b/142228704): this is to be re-implemented once proper design is completed
- mView = View.inflate(mContext,
- R.layout.tv_status_bar_audio_recording, null);
- mAppsInfoContainer = mView.findViewById(R.id.container);
+ private void onStartedRecording(String packageName) {
+ if (!mAudioRecordingApps.add(packageName)) {
+ // This app is already known to perform recording
+ return;
+ }
+
+ switch (mState) {
+ case STATE_NOT_SHOWN:
+ show(packageName);
+ break;
+
+ case STATE_MINIMIZED:
+ expand(packageName);
+ break;
+
+ case STATE_DISAPPEARING:
+ case STATE_APPEARING:
+ case STATE_MAXIMIZING:
+ case STATE_SHOWN:
+ case STATE_MINIMIZING:
+ // Currently animating or expanded. Thus add to the pending notifications, and it
+ // will be picked up once the indicator comes to the STATE_MINIMIZED.
+ mPendingNotifications.add(packageName);
+ break;
+ }
+ }
+
+ private void onDoneRecording(String packageName) {
+ if (!mAudioRecordingApps.remove(packageName)) {
+ // Was not marked as an active recorder, do nothing
+ return;
+ }
+
+ // If not MINIMIZED, will check whether the indicator should be hidden when the indicator
+ // comes to the STATE_MINIMIZED eventually. If is in the STATE_MINIMIZED, but there are
+ // other active recorders - simply ignore.
+ if (mState == STATE_MINIMIZED && mAudioRecordingApps.isEmpty()) {
+ hide();
+ }
+ }
+
+ private void show(String packageName) {
+ // Inflate the indicator view
+ mIndicatorView = LayoutInflater.from(mContext).inflate(
+ R.layout.tv_audio_recording_indicator,
+ null);
+ mIconTextsContainer = mIndicatorView.findViewById(R.id.icon_texts_container);
+ mIconContainerBg = mIconTextsContainer.findViewById(R.id.icon_container_bg);
+ mIcon = mIconTextsContainer.findViewById(R.id.icon_mic);
+ mTextsContainers = mIconTextsContainer.findViewById(R.id.texts_container);
+ mTextView = mTextsContainers.findViewById(R.id.text);
+ mBgRight = mIndicatorView.findViewById(R.id.bg_right);
+
+ // Set up the notification text
+ final String label = getApplicationLabel(packageName);
+ mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
+
+ // Initially change the visibility to INVISIBLE, wait until and receives the size and
+ // then animate it moving from "off" the screen correctly
+ mIndicatorView.setVisibility(View.INVISIBLE);
+ mIndicatorView
+ .getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ // Remove the observer
+ mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
+ this);
+
+ // Now that the width of the indicator has been assigned, we can
+ // move it in from off the screen.
+ final int initialOffset = mIndicatorView.getWidth();
+ final AnimatorSet set = new AnimatorSet();
+ set.setDuration(ANIMATION_DURATION);
+ set.playTogether(
+ ObjectAnimator.ofFloat(mIndicatorView,
+ View.TRANSLATION_X, initialOffset, 0),
+ ObjectAnimator.ofFloat(mIndicatorView, View.ALPHA, 0f,
+ 1f));
+ set.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation,
+ boolean isReverse) {
+ // Indicator is INVISIBLE at the moment, change it.
+ mIndicatorView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ startPulsatingAnimation();
+ onExpanded();
+ }
+ });
+ set.start();
+ }
+ });
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
- MATCH_PARENT,
+ WRAP_CONTENT,
WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.BOTTOM;
+ layoutParams.gravity = Gravity.TOP | Gravity.RIGHT;
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
layoutParams.packageName = mContext.getPackageName();
-
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
- windowManager.addView(mView, layoutParams);
+ windowManager.addView(mIndicatorView, layoutParams);
+
+ mState = STATE_APPEARING;
+ }
- // Set invisible first until it gains its actual size and we are able to hide it by moving
- // off the screen
- mView.setVisibility(View.INVISIBLE);
- mView.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
+ private void expand(String packageName) {
+ final String label = getApplicationLabel(packageName);
+ mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
+
+ final AnimatorSet set = new AnimatorSet();
+ set.playTogether(
+ ObjectAnimator.ofFloat(mIconTextsContainer, View.TRANSLATION_X, 0),
+ ObjectAnimator.ofFloat(mIconContainerBg, View.ALPHA, 1f),
+ ObjectAnimator.ofFloat(mTextsContainers, View.ALPHA, 1f),
+ ObjectAnimator.ofFloat(mBgRight, View.ALPHA, 1f));
+ set.setDuration(ANIMATION_DURATION);
+ set.addListener(
+ new AnimatorListenerAdapter() {
@Override
- public void onGlobalLayout() {
- // Now that we get the height, we can move the bar off ("below") the screen
- final int height = mView.getHeight();
- mView.setTranslationY(height);
- // Remove the observer
- mView.getViewTreeObserver()
- .removeOnGlobalLayoutListener(this);
- // Now, that the view has been measured, and the translation was set to
- // move it off the screen, we change the visibility to GONE
- mView.setVisibility(View.GONE);
+ public void onAnimationEnd(Animator animation) {
+ onExpanded();
}
});
+ set.start();
+
+ mState = STATE_MAXIMIZING;
}
- private void showAudioRecordingDisclosureBar() {
- mView.setVisibility(View.VISIBLE);
- mView.animate()
- .translationY(0f)
- .setDuration(ANIM_DURATION_MS)
- .start();
+ private void minimize() {
+ final int targetOffset = mTextsContainers.getWidth();
+ final AnimatorSet set = new AnimatorSet();
+ set.playTogether(
+ ObjectAnimator.ofFloat(mIconTextsContainer, View.TRANSLATION_X, targetOffset),
+ ObjectAnimator.ofFloat(mIconContainerBg, View.ALPHA, 0f),
+ ObjectAnimator.ofFloat(mTextsContainers, View.ALPHA, 0f),
+ ObjectAnimator.ofFloat(mBgRight, View.ALPHA, 0f));
+ set.setDuration(ANIMATION_DURATION);
+ set.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onMinimized();
+ }
+ });
+ set.start();
+
+ mState = STATE_MINIMIZING;
}
- private void addToAudioRecordingDisclosureBar(String packageName) {
- final PackageManager pm = mContext.getPackageManager();
- final ApplicationInfo appInfo;
- try {
- appInfo = pm.getApplicationInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- return;
+ private void hide() {
+ final int targetOffset =
+ mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX();
+ final AnimatorSet set = new AnimatorSet();
+ set.playTogether(
+ ObjectAnimator.ofFloat(mIndicatorView, View.TRANSLATION_X, targetOffset),
+ ObjectAnimator.ofFloat(mIcon, View.ALPHA, 0f));
+ set.setDuration(ANIMATION_DURATION);
+ set.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onHidden();
+ }
+ });
+ set.start();
+
+ mState = STATE_DISAPPEARING;
+ }
+
+ private void onExpanded() {
+ mState = STATE_SHOWN;
+
+ mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
+ }
+
+ private void onMinimized() {
+ mState = STATE_MINIMIZED;
+
+ if (!mPendingNotifications.isEmpty()) {
+ // There is a new application that started recording, tell the user about it.
+ expand(mPendingNotifications.poll());
+ } else if (mAudioRecordingApps.isEmpty()) {
+ // Nobody is recording anymore, remove the indicator.
+ hide();
}
- final CharSequence label = pm.getApplicationLabel(appInfo);
- final Drawable icon = pm.getApplicationIcon(appInfo);
+ }
+
+ private void onHidden() {
+ final WindowManager windowManager = (WindowManager) mContext.getSystemService(
+ Context.WINDOW_SERVICE);
+ windowManager.removeView(mIndicatorView);
- final View view = LayoutInflater.from(mContext).inflate(R.layout.tv_item_app_info,
- mAppsInfoContainer, false);
- ((TextView) view.findViewById(R.id.title)).setText(label);
- ((ImageView) view.findViewById(R.id.icon)).setImageDrawable(icon);
+ mIndicatorView = null;
+ mIconTextsContainer = null;
+ mIconContainerBg = null;
+ mIcon = null;
+ mTextsContainers = null;
+ mTextView = null;
+ mBgRight = null;
- mAppsInfoContainer.addView(view);
+ mState = STATE_NOT_SHOWN;
}
- private void removeFromAudioRecordingDisclosureBar(int index) {
- mAppsInfoContainer.removeViewAt(index);
+ private void startPulsatingAnimation() {
+ final View pulsatingView = mIconTextsContainer.findViewById(R.id.pulsating_circle);
+ final ObjectAnimator animator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ pulsatingView,
+ PropertyValuesHolder.ofFloat(View.SCALE_X, PULSE_SCALE),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, PULSE_SCALE));
+ animator.setDuration(PULSE_BIT_DURATION);
+ animator.setRepeatCount(ObjectAnimator.INFINITE);
+ animator.setRepeatMode(ObjectAnimator.REVERSE);
+ animator.start();
}
- private void hideAudioRecordingDisclosureBar() {
- final ViewPropertyAnimator animator = mView.animate();
- animator.translationY(mView.getHeight())
- .setDuration(ANIM_DURATION_MS)
- .withEndAction(() -> mView.setVisibility(View.GONE))
- .start();
+ private String getApplicationLabel(String packageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ final ApplicationInfo appInfo;
+ try {
+ appInfo = pm.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return packageName;
+ }
+ return pm.getApplicationLabel(appInfo).toString();
}
private class OnActiveRecordingListener implements AppOpsManager.OnOpActiveChangedListener {
- private final List<String> mExemptApps;
+ private final Set<String> mExemptApps;
private OnActiveRecordingListener() {
- mExemptApps = Arrays.asList(mContext.getResources().getStringArray(
- R.array.audio_recording_disclosure_exempt_apps));
+ mExemptApps = new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(
+ R.array.audio_recording_disclosure_exempt_apps)));
}
@Override
public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
if (DEBUG) {
Log.d(TAG,
- "OP_RECORD_AUDIO active change, active=" + active + ", app=" + packageName);
+ "OP_RECORD_AUDIO active change, active=" + active + ", app="
+ + packageName);
}
if (mExemptApps.contains(packageName)) {
@@ -174,37 +375,10 @@ class AudioRecordingDisclosureBar {
return;
}
- final boolean alreadyTracking = mAudioRecordingApps.contains(packageName);
- if ((active && alreadyTracking) || (!active && !alreadyTracking)) {
- if (DEBUG) {
- Log.d(TAG, "\t- nothing changed");
- }
- return;
- }
-
if (active) {
- if (DEBUG) {
- Log.d(TAG, "\t- new recording app");
- }
-
- if (mAudioRecordingApps.isEmpty()) {
- showAudioRecordingDisclosureBar();
- }
-
- mAudioRecordingApps.add(packageName);
- addToAudioRecordingDisclosureBar(packageName);
+ onStartedRecording(packageName);
} else {
- if (DEBUG) {
- Log.d(TAG, "\t- not recording any more");
- }
-
- final int index = mAudioRecordingApps.indexOf(packageName);
- removeFromAudioRecordingDisclosureBar(index);
- mAudioRecordingApps.remove(index);
-
- if (mAudioRecordingApps.isEmpty()) {
- hideAudioRecordingDisclosureBar();
- }
+ onDoneRecording(packageName);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 2242c1adb1d4..42fbf59fef44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -93,7 +93,7 @@ class BroadcastDispatcherTest : SysuiTestCase() {
// These should be valid filters
`when`(intentFilter.countActions()).thenReturn(1)
`when`(intentFilterOther.countActions()).thenReturn(1)
- `when`(mockContext.user).thenReturn(user0)
+ setUserMock(mockContext, user0)
}
@Test
@@ -140,6 +140,18 @@ class BroadcastDispatcherTest : SysuiTestCase() {
verify(mockUBRUser1, never()).unregisterReceiver(broadcastReceiver)
}
+ @Test
+ fun testRegisterCurrentAsActualUser() {
+ setUserMock(mockContext, user1)
+ broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler,
+ UserHandle.CURRENT)
+
+ testableLooper.processAllMessages()
+
+ verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
+ assertSame(broadcastReceiver, argumentCaptor.value.receiver)
+ }
+
@Test(expected = IllegalArgumentException::class)
fun testFilterMustContainActions() {
val testFilter = IntentFilter()
@@ -186,6 +198,11 @@ class BroadcastDispatcherTest : SysuiTestCase() {
broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
}
+ private fun setUserMock(mockContext: Context, user: UserHandle) {
+ `when`(mockContext.user).thenReturn(user)
+ `when`(mockContext.userId).thenReturn(user.identifier)
+ }
+
private class TestBroadcastDispatcher(
context: Context,
mainHandler: Handler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index ae43aa2f4118..e0d31d053f76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -479,7 +479,7 @@ public class BubbleControllerTest extends SysuiTestCase {
}
@Test
- public void testAutoExpand_FailsNotForeground() {
+ public void testAutoExpand_fails_noFlag() {
assertFalse(mBubbleController.isStackExpanded());
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
@@ -498,7 +498,7 @@ public class BubbleControllerTest extends SysuiTestCase {
}
@Test
- public void testAutoExpand_SucceedsForeground() {
+ public void testAutoExpand_succeeds_withFlag() {
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
@@ -516,38 +516,52 @@ public class BubbleControllerTest extends SysuiTestCase {
}
@Test
- public void testSuppressNotif_FailsNotForeground() {
+ public void testSuppressNotif_onInitialNotif() {
setMetadataFlags(mRow.getEntry(),
- Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, false /* enableFlag */);
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
// Add the suppress notif bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
- // Should not be suppressed because we weren't forground
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ // Notif should be suppressed because we were foreground
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
+ // Dot + flyout is hidden because notif is suppressed
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
}
@Test
- public void testSuppressNotif_SucceedsForeground() {
+ public void testSuppressNotif_onUpdateNotif() {
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // Should not be suppressed
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+ // Should show dot
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+ // Update to suppress notif
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
-
- // Add the suppress notif bubble
- mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
- // Notif should be suppressed because we were foreground
+ // Notif should be suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
+ // Dot + flyout is hidden because notif is suppressed
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
}
+
@Test
public void testExpandStackAndSelectBubble_removedFirst() {
final String key = mRow.getEntry().getKey();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 95c7af31865b..1554abcec3b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.bubbles.BubbleData.TimeSource;
import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.google.common.collect.ImmutableList;
@@ -119,7 +120,9 @@ public class BubbleDataTest extends SysuiTestCase {
.setVisuallyInterruptive(true)
.build();
+ ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
+ mEntryDismissed.setRow(row);
mBubbleA1 = new Bubble(mContext, mEntryA1);
mBubbleA2 = new Bubble(mContext, mEntryA2);
@@ -192,9 +195,8 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryUpdated(mEntryInterruptive, /* suppressFlyout */
- false, /* showInShade */
- true);
+ mBubbleData.notificationEntryUpdated(mEntryInterruptive,
+ false /* suppressFlyout */, true /* showInShade */);
// Verify
verifyUpdateReceived();
@@ -208,12 +210,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryUpdated(mEntryC1, /* suppressFlyout */ false, /* showInShade */
- true);
+ mBubbleData.notificationEntryUpdated(mEntryC1, false /* suppressFlyout */,
+ true /* showInShade */);
verifyUpdateReceived();
- mBubbleData.notificationEntryUpdated(mEntryC1, /* suppressFlyout */ false, /* showInShade */
- true);
+ mBubbleData.notificationEntryUpdated(mEntryC1, false /* suppressFlyout */,
+ true /* showInShade */);
verifyUpdateReceived();
// Verify
@@ -225,16 +227,18 @@ public class BubbleDataTest extends SysuiTestCase {
public void sameUpdate_NotInShade_showFlyout() {
// Setup
mBubbleData.setListener(mListener);
- setMetadataFlags(mEntryDismissed,
- Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag */ true);
// Test
- mBubbleData.notificationEntryUpdated(mEntryDismissed, /* suppressFlyout */ false,
- /* showInShade */ false);
+ mBubbleData.notificationEntryUpdated(mEntryDismissed, false /* suppressFlyout */,
+ false /* showInShade */);
verifyUpdateReceived();
- mBubbleData.notificationEntryUpdated(mEntryDismissed, /* suppressFlyout */
- false, /* showInShade */ false);
+ // Make it look like user swiped away row
+ mEntryDismissed.getRow().dismiss(false /* refocusOnDismiss */);
+ assertThat(mBubbleData.getBubbleWithKey(mEntryDismissed.getKey()).showInShade()).isFalse();
+
+ mBubbleData.notificationEntryUpdated(mEntryDismissed, false /* suppressFlyout */,
+ false /* showInShade */);
verifyUpdateReceived();
// Verify
@@ -936,23 +940,6 @@ public class BubbleDataTest extends SysuiTestCase {
}
/**
- * Sets the bubble metadata flags for this entry. These flags are normally set by
- * NotificationManagerService when the notification is sent, however, these tests do not
- * go through that path so we set them explicitly when testing.
- */
- private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
- Notification.BubbleMetadata bubbleMetadata =
- entry.getSbn().getNotification().getBubbleMetadata();
- int flags = bubbleMetadata.getFlags();
- if (enableFlag) {
- flags |= flag;
- } else {
- flags &= ~flag;
- }
- bubbleMetadata.setFlags(flags);
- }
-
- /**
* No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
* required for BubbleData functionality and verification. NotificationTestHelper is used only
* as a convenience to create a Notification w/BubbleMetadata.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
index 378bba1afda3..1e8ebeafce8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
@@ -48,7 +48,7 @@ public class SysuiLogTest extends SysuiTestCase {
@Test
public void testLogDisabled_noLogsWritten() {
- mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, false);
+ mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, false, false);
assertEquals(mSysuiLog.mTimeline, null);
mSysuiLog.log(new Event("msg"));
@@ -57,7 +57,7 @@ public class SysuiLogTest extends SysuiTestCase {
@Test
public void testLogEnabled_logWritten() {
- mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+ mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true, false);
assertEquals(mSysuiLog.mTimeline.size(), 0);
mSysuiLog.log(new Event("msg"));
@@ -66,7 +66,7 @@ public class SysuiLogTest extends SysuiTestCase {
@Test
public void testMaxLogs() {
- mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+ mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true, false);
assertEquals(mSysuiLog.mTimeline.size(), 0);
final String msg = "msg";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 39ce8c1209ac..8a9a7a28efb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -120,7 +120,6 @@ public class TileQueryHelperTest extends SysuiTestCase {
).when(mQSTileHost).createTile(anyString());
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
mMainExecutor = new FakeExecutor(clock);
mBgExecutor = new FakeExecutor(clock);
mTileQueryHelper = new TileQueryHelper(mContext, mMainExecutor, mBgExecutor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
index a25af84ca28e..ffaea156c9d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
@@ -690,7 +690,6 @@ public class NotifListBuilderImplTest extends SysuiTestCase {
mListBuilder.addFilter(filter3);
// GIVEN the SystemClock is set to a particular time:
- mSystemClock.setAutoIncrement(true);
mSystemClock.setUptimeMillis(47);
// WHEN the pipeline is kicked off on a list of notifs
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
index b1716ff47129..bd641243be12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -49,7 +49,6 @@ public class FakeExecutorTest extends SysuiTestCase {
@Test
public void testNoDelay() {
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
FakeExecutor fakeExecutor = new FakeExecutor(clock);
RunnableImpl runnable = new RunnableImpl();
@@ -99,7 +98,6 @@ public class FakeExecutorTest extends SysuiTestCase {
@Test
public void testDelayed() {
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
FakeExecutor fakeExecutor = new FakeExecutor(clock);
RunnableImpl runnable = new RunnableImpl();
@@ -134,7 +132,6 @@ public class FakeExecutorTest extends SysuiTestCase {
@Test
public void testDelayed_AdvanceAndRun() {
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
FakeExecutor fakeExecutor = new FakeExecutor(clock);
RunnableImpl runnable = new RunnableImpl();
@@ -181,7 +178,6 @@ public class FakeExecutorTest extends SysuiTestCase {
@Test
public void testExecutionOrder() {
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
FakeExecutor fakeExecutor = new FakeExecutor(clock);
RunnableImpl runnableA = new RunnableImpl();
RunnableImpl runnableB = new RunnableImpl();
@@ -251,7 +247,6 @@ public class FakeExecutorTest extends SysuiTestCase {
@Test
public void testRemoval_single() {
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
FakeExecutor fakeExecutor = new FakeExecutor(clock);
RunnableImpl runnable = new RunnableImpl();
Runnable removeFunction;
@@ -291,7 +286,6 @@ public class FakeExecutorTest extends SysuiTestCase {
@Test
public void testRemoval_multi() {
FakeSystemClock clock = new FakeSystemClock();
- clock.setAutoIncrement(false);
FakeExecutor fakeExecutor = new FakeExecutor(clock);
List<Runnable> removeFunctions = new ArrayList<>();
RunnableImpl runnable = new RunnableImpl();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index 65e5902c84df..e94eaafdb692 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -20,8 +20,6 @@ import java.util.ArrayList;
import java.util.List;
public class FakeSystemClock implements SystemClock {
- private boolean mAutoIncrement = true;
-
private long mUptimeMillis;
private long mElapsedRealtime;
private long mElapsedRealtimeNanos;
@@ -34,54 +32,36 @@ public class FakeSystemClock implements SystemClock {
@Override
public long uptimeMillis() {
long value = mUptimeMillis;
- if (mAutoIncrement) {
- setUptimeMillis(mUptimeMillis + 1);
- }
return value;
}
@Override
public long elapsedRealtime() {
long value = mElapsedRealtime;
- if (mAutoIncrement) {
- setElapsedRealtime(mElapsedRealtime + 1);
- }
return value;
}
@Override
public long elapsedRealtimeNanos() {
long value = mElapsedRealtimeNanos;
- if (mAutoIncrement) {
- setElapsedRealtimeNanos(mElapsedRealtimeNanos + 1);
- }
return value;
}
@Override
public long currentThreadTimeMillis() {
long value = mCurrentThreadTimeMillis;
- if (mAutoIncrement) {
- setCurrentThreadTimeMillis(mCurrentThreadTimeMillis + 1);
- }
return value;
}
@Override
public long currentThreadTimeMicro() {
long value = mCurrentThreadTimeMicro;
- if (mAutoIncrement) {
- setCurrentThreadTimeMicro(mCurrentThreadTimeMicro + 1);
- }
return value;
}
@Override
public long currentTimeMicro() {
long value = mCurrentTimeMicro;
- if (mAutoIncrement) {
- setCurrentTimeMicro(mCurrentTimeMicro + 1);
- }
return value;
}
@@ -127,11 +107,6 @@ public class FakeSystemClock implements SystemClock {
}
}
- /** If true, each call to get____ will be one higher than the previous call to that method. */
- public void setAutoIncrement(boolean autoIncrement) {
- mAutoIncrement = autoIncrement;
- }
-
public void addListener(ClockTickListener listener) {
mListeners.add(listener);
}
diff --git a/services/Android.bp b/services/Android.bp
index 3b566078bd51..fd4094f2a7c2 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -14,6 +14,7 @@ filegroup {
":services.appwidget-sources",
":services.autofill-sources",
":services.backup-sources",
+ ":backuplib-sources",
":services.companion-sources",
":services.contentcapture-sources",
":services.contentsuggestions-sources",
@@ -101,3 +102,29 @@ filegroup {
name: "art-profile",
srcs: ["art-profile"],
}
+
+// API stub
+// =============================================================
+
+droidstubs {
+ name: "services-stubs.sources",
+ srcs: [":services-sources"],
+ installable: false,
+ // TODO: remove the --hide options below
+ args: " --show-single-annotation android.annotation.SystemApi" +
+ " --hide-annotation android.annotation.Hide" +
+ " --hide-package com.google.android.startop.iorap" +
+ " --hide ReferencesHidden" +
+ " --hide DeprecationMismatch" +
+ " --hide HiddenTypedefConstant",
+ libs: [
+ "framework-all",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "services-stubs",
+ srcs: [":services-stubs.sources"],
+ installable: false,
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7fdd83bebbcd..6a6e2b2f3467 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,11 @@
package com.android.server.accessibility;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -31,6 +36,7 @@ import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManagerInternal;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -70,6 +76,7 @@ import android.provider.Settings;
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -113,9 +120,9 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* This class is instantiated by the system as a system level service and can be
@@ -754,6 +761,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
userState.setDisplayMagnificationEnabledLocked(false);
userState.setNavBarMagnificationEnabledLocked(false);
+ userState.disableShortcutMagnificationLocked();
+
userState.setAutoclickEnabledLocked(false);
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
@@ -1072,6 +1081,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ // TODO(a11y shortcut): Remove this function and Use #performAccessibilityShortcutInternal(
+ // ACCESSIBILITY_BUTTON) instead, after the new Settings shortcut Ui merged.
private void notifyAccessibilityButtonClickedLocked(int displayId) {
final AccessibilityUserState state = getCurrentUserStateLocked();
@@ -1105,8 +1116,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (state.getServiceAssignedToAccessibilityButtonLocked() == null
&& !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
- displayId));
+ AccessibilityManagerService::showAccessibilityTargetsSelection, this,
+ displayId, ACCESSIBILITY_BUTTON));
} else if (state.isNavBarMagnificationEnabledLocked()
&& state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
mMainHandler.sendMessage(obtainMessage(
@@ -1125,8 +1136,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// The user may have turned off the assigned service or feature
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
- displayId));
+ AccessibilityManagerService::showAccessibilityTargetsSelection, this,
+ displayId, ACCESSIBILITY_BUTTON));
}
}
@@ -1138,13 +1149,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void showAccessibilityButtonTargetSelection(int displayId) {
+ private void showAccessibilityTargetsSelection(int displayId,
+ @ShortcutType int shortcutType) {
Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+ bundle.putInt(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType);
mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
}
+ private void launchShortcutTargetActivity(int displayId, ComponentName name) {
+ final Intent intent = new Intent();
+ final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+ intent.setComponent(name);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ try {
+ mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
+ } catch (ActivityNotFoundException ignore) {
+ // ignore the exception
+ }
+ }
+
private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
final AccessibilityUserState state = getCurrentUserStateLocked();
mIsAccessibilityButtonShown = available;
@@ -1353,9 +1378,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
private void readComponentNamesFromSettingLocked(String settingName, int userId,
Set<ComponentName> outComponentNames) {
- String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- settingName, userId);
- readComponentNamesFromStringLocked(settingValue, outComponentNames, false);
+ readColonDelimitedSettingToSet(settingName, userId, outComponentNames,
+ str -> ComponentName.unflattenFromString(str));
}
/**
@@ -1370,34 +1394,57 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void readComponentNamesFromStringLocked(String names,
Set<ComponentName> outComponentNames,
boolean doMerge) {
+ readColonDelimitedStringToSet(names, outComponentNames, doMerge,
+ str -> ComponentName.unflattenFromString(str));
+ }
+
+ @Override
+ public void persistComponentNamesToSettingLocked(String settingName,
+ Set<ComponentName> componentNames, int userId) {
+ persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
+ componentName -> componentName.flattenToShortString());
+ }
+
+ private <T> void readColonDelimitedSettingToSet(String settingName, int userId, Set<T> outSet,
+ Function<String, T> toItem) {
+ final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ settingName, userId);
+ readColonDelimitedStringToSet(settingValue, outSet, false, toItem);
+ }
+
+ private <T> void readColonDelimitedStringToSet(String names, Set<T> outSet, boolean doMerge,
+ Function<String, T> toItem) {
if (!doMerge) {
- outComponentNames.clear();
+ outSet.clear();
}
- if (names != null) {
- TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ if (!TextUtils.isEmpty(names)) {
+ final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(names);
while (splitter.hasNext()) {
- String str = splitter.next();
- if (str == null || str.length() <= 0) {
+ final String str = splitter.next();
+ if (TextUtils.isEmpty(str)) {
continue;
}
- ComponentName enabledService = ComponentName.unflattenFromString(str);
- if (enabledService != null) {
- outComponentNames.add(enabledService);
+ final T item = toItem.apply(str);
+ if (item != null) {
+ outSet.add(item);
}
}
}
}
- @Override
- public void persistComponentNamesToSettingLocked(String settingName,
- Set<ComponentName> componentNames, int userId) {
- StringBuilder builder = new StringBuilder();
- for (ComponentName componentName : componentNames) {
+ private <T> void persistColonDelimitedSetToSettingLocked(String settingName, int userId,
+ Set<T> set, Function<T, String> toString) {
+ final StringBuilder builder = new StringBuilder();
+ for (T item : set) {
+ final String str = (item != null ? toString.apply(item) : null);
+ if (TextUtils.isEmpty(str)) {
+ continue;
+ }
if (builder.length() > 0) {
builder.append(COMPONENT_NAME_SEPARATOR);
}
- builder.append(componentName.flattenToShortString());
+ builder.append(str);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -1537,7 +1584,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (userState.isDisplayMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
- if (userState.isNavBarMagnificationEnabledLocked()) {
+ if (userState.isNavBarMagnificationEnabledLocked()
+ || userState.isShortcutKeyMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
}
if (userHasMagnificationServicesLocked(userState)) {
@@ -1647,7 +1695,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mInitialized = true;
updateLegacyCapabilitiesLocked(userState);
updateServicesLocked(userState);
- updateAccessibilityShortcutLocked(userState);
updateWindowsForAccessibilityCallbackLocked(userState);
updateFilterKeyEventsLocked(userState);
updateTouchExplorationLocked(userState);
@@ -1657,6 +1704,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
scheduleUpdateInputFilter(userState);
updateRelevantEventsLocked(userState);
scheduleUpdateClientsIfNeededLocked(userState);
+ updateAccessibilityShortcutKeyTargetsLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
}
@@ -1751,7 +1799,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readHighTextContrastEnabledSettingLocked(userState);
somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
- somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
+ somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
return somethingChanged;
@@ -1847,57 +1895,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) {
- String componentNameToEnableString = AccessibilityShortcutController
- .getTargetServiceComponentNameString(mContext, userState.mUserId);
- if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
- if (userState.getServiceToEnableWithShortcutLocked() == null) {
- return false;
+ private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
+ final Set<String> targetsFromSetting = new ArraySet<>();
+ readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ userState.mUserId, targetsFromSetting, str -> str);
+ if (targetsFromSetting.isEmpty()) {
+ // Fall back to device's default a11y service.
+ final String defaultService = mContext.getString(
+ R.string.config_defaultAccessibilityService);
+ if (!TextUtils.isEmpty(defaultService)) {
+ targetsFromSetting.add(defaultService);
}
- userState.setServiceToEnableWithShortcutLocked(null);
- return true;
}
- ComponentName componentNameToEnable =
- ComponentName.unflattenFromString(componentNameToEnableString);
- if ((componentNameToEnable != null)
- && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) {
+
+ final Set<String> currentTargets =
+ userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY);
+ if (targetsFromSetting.equals(currentTargets)) {
return false;
}
-
- userState.setServiceToEnableWithShortcutLocked(componentNameToEnable);
+ currentTargets.clear();
+ currentTargets.addAll(targetsFromSetting);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
return true;
}
private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
- String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
- if (TextUtils.isEmpty(componentId)) {
- if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null)
- && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
- return false;
- }
- userState.setServiceAssignedToAccessibilityButtonLocked(null);
- userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
- return true;
- }
-
- if (componentId.equals(MagnificationController.class.getName())) {
- if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
- return false;
- }
- userState.setServiceAssignedToAccessibilityButtonLocked(null);
- userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
- return true;
- }
+ final Set<String> targetsFromSetting = new ArraySet<>();
+ readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ userState.mUserId, targetsFromSetting, str -> str);
- ComponentName componentName = ComponentName.unflattenFromString(componentId);
- if (Objects.equals(componentName,
- userState.getServiceAssignedToAccessibilityButtonLocked())) {
+ final Set<String> currentTargets =
+ userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
+ if (targetsFromSetting.equals(currentTargets)) {
return false;
}
- userState.setServiceAssignedToAccessibilityButtonLocked(componentName);
- userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
+ currentTargets.clear();
+ currentTargets.addAll(targetsFromSetting);
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
return true;
}
@@ -1921,34 +1955,33 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
- * Check if the service that will be enabled by the shortcut is installed. If it isn't,
- * clear the value and the associated setting so a sideloaded service can't spoof the
- * package name of the default service.
- *
- * @param userState
+ * Check if the targets that will be enabled by the accessibility shortcut key is installed.
+ * If it isn't, remove it from the list and associated setting so a side loaded service can't
+ * spoof the package name of the default service.
*/
- private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) {
- if (userState.getServiceToEnableWithShortcutLocked() == null) {
+ private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) {
+ final Set<String> currentTargets =
+ userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY);
+ final int lastSize = currentTargets.size();
+ if (lastSize == 0) {
return;
}
- boolean shortcutServiceIsInstalled =
- AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
- .containsKey(userState.getServiceToEnableWithShortcutLocked());
- for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
- i++) {
- if (userState.mInstalledServices.get(i).getComponentName()
- .equals(userState.getServiceToEnableWithShortcutLocked())) {
- shortcutServiceIsInstalled = true;
- }
+ currentTargets.removeIf(
+ name -> !userState.isShortcutTargetInstalledLocked(name));
+ if (lastSize == currentTargets.size()) {
+ return;
}
- if (!shortcutServiceIsInstalled) {
- userState.setServiceToEnableWithShortcutLocked(null);
+
+ // Update setting key with new value.
+ persistColonDelimitedSetToSettingLocked(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ userState.mUserId, currentTargets, str -> str);
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+
+ // Disable accessibility shortcut key if there's no shortcut installed.
+ if (currentTargets.isEmpty()) {
final long identity = Binder.clearCallingIdentity();
try {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null,
- userState.mUserId);
-
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId);
} finally {
@@ -2004,7 +2037,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
if (userState.isDisplayMagnificationEnabledLocked()
- || userState.isNavBarMagnificationEnabledLocked()) {
+ || userState.isNavBarMagnificationEnabledLocked()
+ || userState.isShortcutKeyMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
getMagnificationController().register(display.getDisplayId());
@@ -2088,7 +2122,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ /**
+ * 1) Update accessibility button availability to accessibility services.
+ * 2) Check if the targets that will be enabled by the accessibility button is installed.
+ * If it isn't, remove it from the list and associated setting so a side loaded service can't
+ * spoof the package name of the default service.
+ */
private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
+ // Update accessibility button availability.
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
@@ -2096,6 +2137,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
service.isAccessibilityButtonAvailableLocked(userState));
}
}
+
+ final Set<String> currentTargets =
+ userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
+ final int lastSize = currentTargets.size();
+ if (lastSize == 0) {
+ return;
+ }
+ currentTargets.removeIf(
+ name -> !userState.isShortcutTargetInstalledLocked(name));
+ if (lastSize == currentTargets.size()) {
+ return;
+ }
+
+ // Update setting key with new value.
+ persistColonDelimitedSetToSettingLocked(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ userState.mUserId, currentTargets, str -> str);
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
@@ -2156,7 +2215,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
- * AIDL-exposed method to be called when the accessibility shortcut is enabled. Requires
+ * AIDL-exposed method to be called when the accessibility shortcut key is enabled. Requires
* permission to write secure settings, since someone with that permission can enable
* accessibility services themselves.
*/
@@ -2168,52 +2227,177 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
throw new SecurityException(
"performAccessibilityShortcut requires the MANAGE_ACCESSIBILITY permission");
}
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::performAccessibilityShortcutInternal, this,
+ Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY));
+ }
+
+ /**
+ * Perform the accessibility shortcut action.
+ *
+ * @param shortcutType The shortcut type.
+ * @param displayId The display id of the accessibility button.
+ */
+ private void performAccessibilityShortcutInternal(int displayId,
+ @ShortcutType int shortcutType) {
+ final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType);
+ if (shortcutTargets.isEmpty()) {
+ Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType);
+ return;
+ }
+ // In case there are many targets assigned to the given shortcut.
+ if (shortcutTargets.size() > 1) {
+ showAccessibilityTargetsSelection(displayId, shortcutType);
+ return;
+ }
+ final String targetName = shortcutTargets.get(0);
+ // In case user assigned magnification to the given shortcut.
+ if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
+ sendAccessibilityButtonToInputFilter(displayId);
+ return;
+ }
+ final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName);
+ if (targetComponentName == null) {
+ Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
+ return;
+ }
+ // In case user assigned an accessibility framework feature to the given shortcut.
+ if (performAccessibilityFrameworkFeature(targetComponentName)) {
+ return;
+ }
+ // In case user assigned an accessibility shortcut target to the given shortcut.
+ if (performAccessibilityShortcutTargetActivity(displayId, targetComponentName)) {
+ return;
+ }
+ // in case user assigned an accessibility service to the given shortcut.
+ if (performAccessibilityShortcutTargetService(
+ displayId, shortcutType, targetComponentName)) {
+ return;
+ }
+ }
+
+ private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget) {
final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
- synchronized(mLock) {
- final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
- final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked();
- if (serviceName == null) {
- return;
- }
- if (frameworkFeatureMap.containsKey(serviceName)) {
- // Toggle the requested framework feature
- ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(serviceName);
- SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
- featureInfo.getSettingKey(), mCurrentUserId);
- // Assuming that the default state will be to have the feature off
- if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
- setting.write(featureInfo.getSettingOnValue());
- } else {
- setting.write(featureInfo.getSettingOffValue());
+ if (!frameworkFeatureMap.containsKey(assignedTarget)) {
+ return false;
+ }
+ // Toggle the requested framework feature
+ final ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
+ final SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
+ featureInfo.getSettingKey(), mCurrentUserId);
+ // Assuming that the default state will be to have the feature off
+ if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
+ setting.write(featureInfo.getSettingOnValue());
+ } else {
+ setting.write(featureInfo.getSettingOffValue());
+ }
+ return true;
+ }
+
+ private boolean performAccessibilityShortcutTargetActivity(int displayId,
+ ComponentName assignedTarget) {
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ for (int i = 0; i < userState.mInstalledShortcuts.size(); i++) {
+ final AccessibilityShortcutInfo shortcutInfo = userState.mInstalledShortcuts.get(i);
+ if (!shortcutInfo.getComponentName().equals(assignedTarget)) {
+ continue;
}
+ launchShortcutTargetActivity(displayId, assignedTarget);
+ return true;
}
- final long identity = Binder.clearCallingIdentity();
- try {
- if (userState.mComponentNameToServiceMap.get(serviceName) == null) {
- enableAccessibilityServiceLocked(serviceName, mCurrentUserId);
+ }
+ return false;
+ }
+
+ /**
+ * Perform accessibility service shortcut action.
+ *
+ * 1) For {@link AccessibilityManager#ACCESSIBILITY_BUTTON} type and services targeting sdk
+ * version <= Q: callbacks to accessibility service if service is bounded and requests
+ * accessibility button.
+ * 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
+ * version <= Q: turns on / off the accessibility service.
+ * 3) For services targeting sdk version > Q:
+ * a) Turns on / off the accessibility service, if service does not request accessibility
+ * button.
+ * b) Callbacks to accessibility service if service is bounded and requests accessibility
+ * button.
+ */
+ private boolean performAccessibilityShortcutTargetService(int displayId,
+ @ShortcutType int shortcutType, ComponentName assignedTarget) {
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ final AccessibilityServiceInfo installedServiceInfo =
+ userState.getInstalledServiceInfoLocked(assignedTarget);
+ if (installedServiceInfo == null) {
+ Slog.d(LOG_TAG, "Perform shortcut failed, invalid component name:"
+ + assignedTarget);
+ return false;
+ }
+
+ final AccessibilityServiceConnection serviceConnection =
+ userState.getServiceConnectionLocked(assignedTarget);
+ final int targetSdk = installedServiceInfo.getResolveInfo()
+ .serviceInfo.applicationInfo.targetSdkVersion;
+ final boolean requestA11yButton = (installedServiceInfo.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ // Turns on / off the accessibility service
+ if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY)
+ || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
+ if (serviceConnection == null) {
+ enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
} else {
- disableAccessibilityServiceLocked(serviceName, mCurrentUserId);
+ disableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
+ return true;
+ }
+ // Callbacks to a11y service if it's bounded and requests a11y button.
+ if (serviceConnection == null
+ || !userState.mBoundServices.contains(serviceConnection)
+ || !serviceConnection.mRequestAccessibilityButton) {
+ Slog.d(LOG_TAG, "Perform shortcut failed, service is not ready:"
+ + assignedTarget);
+ return false;
}
+ serviceConnection.notifyAccessibilityButtonClickedLocked(displayId);
+ return true;
}
- };
+ }
@Override
- public String getAccessibilityShortcutService() {
- if (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
}
- synchronized(mLock) {
- final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
- if (userState.getServiceToEnableWithShortcutLocked() == null) {
- return null;
+ return getAccessibilityShortcutTargetsInternal(shortcutType);
+ }
+
+ private List<String> getAccessibilityShortcutTargetsInternal(@ShortcutType int shortcutType) {
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ final ArrayList<String> shortcutTargets = new ArrayList<>(
+ userState.getShortcutTargetsLocked(shortcutType));
+ if (shortcutType != ACCESSIBILITY_BUTTON) {
+ return shortcutTargets;
+ }
+ // Adds legacy a11y services requesting a11y button into the list.
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if (!service.mRequestAccessibilityButton
+ || service.getServiceInfo().getResolveInfo().serviceInfo.applicationInfo
+ .targetSdkVersion > Build.VERSION_CODES.Q) {
+ continue;
+ }
+ final String serviceName = service.getComponentName().flattenToString();
+ if (!TextUtils.isEmpty(serviceName)) {
+ shortcutTargets.add(serviceName);
+ }
}
- return userState.getServiceToEnableWithShortcutLocked().flattenToString();
+ return shortcutTargets;
}
}
@@ -2609,6 +2793,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ // TODO(a11y shortcut): Remove this setting key, and have a migrate function in
+ // Setting provider after new shortcut UI merged.
private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
@@ -2713,7 +2899,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| mShowImeWithHardKeyboardUri.equals(uri)) {
userState.reconcileSoftKeyboardModeWithSettingsLocked();
} else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
- if (readAccessibilityShortcutSettingLocked(userState)) {
+ if (readAccessibilityShortcutKeySettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
@@ -2724,8 +2910,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| mUserInteractiveUiTimeoutUri.equals(uri)) {
readUserRecommendedUiTimeoutSettingsLocked(userState);
}
- // TODO(a11y shortcut): Monitor new setting keys, when user adds shortcut, and
- // remove from the list of enabled targets anything that's been uninstalled.
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 6cadb6d0f31f..cbff6bdcec77 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -294,6 +294,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
}
+ // TODO(a11y shortcut): Refactoring the logic here, after the new Settings shortcut Ui merged.
public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
// If the service does not request the accessibility button, it isn't available
if (!mRequestAccessibilityButton) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a0b9866e24d2..a163f7434e1f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -22,6 +22,11 @@ import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_K
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -33,10 +38,14 @@ import android.content.Context;
import android.os.Binder;
import android.os.RemoteCallbackList;
import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.accessibility.AccessibilityShortcutController;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -79,19 +88,18 @@ class AccessibilityUserState {
final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
- private final ServiceInfoChangeListener mServiceInfoChangeListener;
+ final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
- private ComponentName mServiceAssignedToAccessibilityButton;
+ final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
- private ComponentName mServiceChangingSoftKeyboardMode;
+ private final ServiceInfoChangeListener mServiceInfoChangeListener;
- private ComponentName mServiceToEnableWithShortcut;
+ private ComponentName mServiceChangingSoftKeyboardMode;
private boolean mBindInstantServiceAllowed;
private boolean mIsAutoclickEnabled;
private boolean mIsDisplayMagnificationEnabled;
private boolean mIsFilterKeyEventsEnabled;
- private boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
private boolean mIsNavBarMagnificationEnabled;
private boolean mIsPerformGesturesEnabled;
private boolean mIsTextHighContrastEnabled;
@@ -141,11 +149,11 @@ class AccessibilityUserState {
// Clear state persisted in settings.
mEnabledServices.clear();
mTouchExplorationGrantedServices.clear();
+ mAccessibilityShortcutKeyTargets.clear();
+ mAccessibilityButtonTargets.clear();
mIsTouchExplorationEnabled = false;
mIsDisplayMagnificationEnabled = false;
mIsNavBarMagnificationEnabled = false;
- mServiceAssignedToAccessibilityButton = null;
- mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
@@ -435,6 +443,26 @@ class AccessibilityUserState {
pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
pw.append("}");
pw.println();
+ pw.append(" shortcut key:{");
+ int size = mAccessibilityShortcutKeyTargets.size();
+ for (int i = 0; i < size; i++) {
+ final String componentId = mAccessibilityShortcutKeyTargets.valueAt(i);
+ pw.append(componentId);
+ if (i + 1 < size) {
+ pw.append(", ");
+ }
+ }
+ pw.println("}");
+ pw.append(" button:{");
+ size = mAccessibilityButtonTargets.size();
+ for (int i = 0; i < size; i++) {
+ final String componentId = mAccessibilityButtonTargets.valueAt(i);
+ pw.append(componentId);
+ if (i + 1 < size) {
+ pw.append(", ");
+ }
+ }
+ pw.println("}");
pw.append(" Bound services:{");
final int serviceCount = mBoundServices.size();
for (int j = 0; j < serviceCount; j++) {
@@ -525,20 +553,85 @@ class AccessibilityUserState {
mLastSentClientState = state;
}
- public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
- return mIsNavBarMagnificationAssignedToAccessibilityButton;
+ public boolean isShortcutKeyMagnificationEnabledLocked() {
+ return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
}
- public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) {
- mIsNavBarMagnificationAssignedToAccessibilityButton = assigned;
+ /**
+ * Disable both shortcuts' magnification function.
+ */
+ public void disableShortcutMagnificationLocked() {
+ mAccessibilityShortcutKeyTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
+ mAccessibilityButtonTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
}
- public boolean isNavBarMagnificationEnabledLocked() {
- return mIsNavBarMagnificationEnabled;
+ /**
+ * Returns a set which contains the flattened component names and the system class names
+ * assigned to the given shortcut.
+ *
+ * @param shortcutType The shortcut type.
+ * @return The array set of the strings
+ */
+ public ArraySet<String> getShortcutTargetsLocked(@ShortcutType int shortcutType) {
+ if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ return mAccessibilityShortcutKeyTargets;
+ } else if (shortcutType == ACCESSIBILITY_BUTTON) {
+ return mAccessibilityButtonTargets;
+ }
+ return null;
}
- public void setNavBarMagnificationEnabledLocked(boolean enabled) {
- mIsNavBarMagnificationEnabled = enabled;
+ /**
+ * Whether or not the given shortcut target is installed in device.
+ *
+ * @param name The shortcut target name
+ * @return true if the shortcut target is installed.
+ */
+ public boolean isShortcutTargetInstalledLocked(String name) {
+ if (TextUtils.isEmpty(name)) {
+ return false;
+ }
+ if (MAGNIFICATION_CONTROLLER_NAME.equals(name)) {
+ return true;
+ }
+
+ final ComponentName componentName = ComponentName.unflattenFromString(name);
+ if (componentName == null) {
+ return false;
+ }
+ if (AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
+ .containsKey(componentName)) {
+ return true;
+ }
+ if (getInstalledServiceInfoLocked(componentName) != null) {
+ return true;
+ }
+ for (int i = 0; i < mInstalledShortcuts.size(); i++) {
+ if (mInstalledShortcuts.get(i).getComponentName().equals(componentName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns installed accessibility service info by the given service component name.
+ */
+ public AccessibilityServiceInfo getInstalledServiceInfoLocked(ComponentName componentName) {
+ for (int i = 0; i < mInstalledServices.size(); i++) {
+ final AccessibilityServiceInfo serviceInfo = mInstalledServices.get(i);
+ if (serviceInfo.getComponentName().equals(componentName)) {
+ return serviceInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns accessibility service connection by the given service component name.
+ */
+ public AccessibilityServiceConnection getServiceConnectionLocked(ComponentName componentName) {
+ return mComponentNameToServiceMap.get(componentName);
}
public int getNonInteractiveUiTimeoutLocked() {
@@ -557,14 +650,6 @@ class AccessibilityUserState {
mIsPerformGesturesEnabled = enabled;
}
- public ComponentName getServiceAssignedToAccessibilityButtonLocked() {
- return mServiceAssignedToAccessibilityButton;
- }
-
- public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) {
- mServiceAssignedToAccessibilityButton = componentName;
- }
-
public ComponentName getServiceChangingSoftKeyboardModeLocked() {
return mServiceChangingSoftKeyboardMode;
}
@@ -574,14 +659,6 @@ class AccessibilityUserState {
mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
}
- public ComponentName getServiceToEnableWithShortcutLocked() {
- return mServiceToEnableWithShortcut;
- }
-
- public void setServiceToEnableWithShortcutLocked(ComponentName componentName) {
- mServiceToEnableWithShortcut = componentName;
- }
-
public boolean isTextHighContrastEnabledLocked() {
return mIsTextHighContrastEnabled;
}
@@ -613,4 +690,28 @@ class AccessibilityUserState {
public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
mUserNonInteractiveUiTimeout = timeout;
}
+
+ // TODO(a11y shortcut): These functions aren't necessary, after the new Settings shortcut Ui
+ // is merged.
+ boolean isNavBarMagnificationEnabledLocked() {
+ return mIsNavBarMagnificationEnabled;
+ }
+
+ void setNavBarMagnificationEnabledLocked(boolean enabled) {
+ mIsNavBarMagnificationEnabled = enabled;
+ }
+
+ boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
+ return mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
+ }
+
+ ComponentName getServiceAssignedToAccessibilityButtonLocked() {
+ final String targetName = mAccessibilityButtonTargets.isEmpty() ? null
+ : mAccessibilityButtonTargets.valueAt(0);
+ if (targetName == null) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(targetName);
+ }
+ // TODO(a11y shortcut): End
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 753c1171aeb3..f8f685d939a7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5596,7 +5596,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
mDnsResolver, mNMS, factorySerialNumber);
// Make sure the network capabilities reflect what the agent info says.
- nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
+ nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSSID() : extraInfo;
@@ -5950,11 +5950,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- final NetworkCapabilities prevNc;
- synchronized (nai) {
- prevNc = nai.networkCapabilities;
- nai.setNetworkCapabilities(newNc);
- }
+ final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
updateUids(nai, prevNc, newNc);
@@ -5963,7 +5959,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// the change we're processing can't affect any requests, it can only affect the listens
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
- processListenRequests(nai, true);
+ processListenRequests(nai);
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -6271,8 +6267,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateAllVpnsCapabilities();
}
- private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
+ private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
// For consistency with previous behaviour, send onLost callbacks before onAvailable.
+ processNewlyLostListenRequests(nai);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ processNewlySatisfiedListenRequests(nai);
+ }
+
+ private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkRequest nr = nri.request;
if (!nr.isListen()) continue;
@@ -6281,11 +6283,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
+ }
- if (capabilitiesChanged) {
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
-
+ private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkRequest nr = nri.request;
if (!nr.isListen()) continue;
@@ -6468,19 +6468,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
- // Second pass: process all listens.
- if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
- // TODO : most of the following is useless because the only thing that changed
- // here is whether the network is a background network. Clean this up.
+ // Finally, process listen requests and update capabilities if the background state has
+ // changed for this network. For consistency with previous behavior, send onLost callbacks
+ // before onAvailable.
+ processNewlyLostListenRequests(newNetwork);
- NetworkCapabilities newNc = mixInCapabilities(newNetwork,
+ // Maybe the network changed background states. Update its capabilities.
+ final boolean backgroundChanged = wasBackgroundNetwork != newNetwork.isBackgroundNetwork();
+ if (backgroundChanged) {
+ final NetworkCapabilities newNc = mixInCapabilities(newNetwork,
newNetwork.networkCapabilities);
- if (Objects.equals(newNetwork.networkCapabilities, newNc)) return;
-
final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
final int newPermission = getNetworkPermission(newNc);
- if (oldPermission != newPermission && newNetwork.created && !newNetwork.isVPN()) {
+ if (oldPermission != newPermission) {
try {
mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
} catch (RemoteException e) {
@@ -6488,53 +6489,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- final NetworkCapabilities prevNc;
- synchronized (newNetwork) {
- prevNc = newNetwork.networkCapabilities;
- newNetwork.setNetworkCapabilities(newNc);
- }
-
- updateUids(newNetwork, prevNc, newNc);
-
- if (newNetwork.getCurrentScore() == score
- && newNc.equalRequestableCapabilities(prevNc)) {
- // If the requestable capabilities haven't changed, and the score hasn't changed,
- // then the change we're processing can't affect any requests, it can only affect
- // the listens on this network.
- processListenRequests(newNetwork, true);
- } else {
- rematchAllNetworksAndRequests();
- notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
-
- if (prevNc != null) {
- final boolean oldMetered = prevNc.isMetered();
- final boolean newMetered = newNc.isMetered();
- final boolean meteredChanged = oldMetered != newMetered;
-
- if (meteredChanged) {
- maybeNotifyNetworkBlocked(newNetwork, oldMetered, newMetered,
- mRestrictBackground, mRestrictBackground);
- }
-
- final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
- != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-
- // Report changes that are interesting for network statistics tracking.
- if (meteredChanged || roamingChanged) {
- notifyIfacesChangedForNetworkStats();
- }
- }
-
- if (!newNc.hasTransport(TRANSPORT_VPN)) {
- // Tell VPNs about updated capabilities, since they may need to
- // bubble those changes through.
- updateAllVpnsCapabilities();
- }
-
- } else {
- processListenRequests(newNetwork, false);
+ newNetwork.getAndSetNetworkCapabilities(newNc);
+ notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+
+ processNewlySatisfiedListenRequests(newNetwork);
}
/**
@@ -6719,9 +6678,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// NetworkCapabilities need to be set before sending the private DNS config to
// NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required.
- synchronized (networkAgent) {
- networkAgent.setNetworkCapabilities(networkAgent.networkCapabilities);
- }
+ networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
+
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
null);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 840b7af19890..0d496b6b427d 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -58,6 +58,7 @@ import android.net.NetworkStack;
import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.TetherConfigParcel;
import android.net.TetherStatsParcel;
import android.net.UidRange;
import android.net.UidRangeParcel;
@@ -1023,7 +1024,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
NetworkStack.checkNetworkStackPermission(mContext);
// an odd number of addrs will fail
try {
- mNetdService.tetherStartWithConfiguration(usingLegacyDnsProxy, dhcpRange);
+ final TetherConfigParcel config = new TetherConfigParcel();
+ config.usingLegacyDnsProxy = usingLegacyDnsProxy;
+ config.dhcpRanges = dhcpRange;
+ mNetdService.tetherStartWithConfiguration(config);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 822fc90cd78e..0a6473ab92d9 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
@@ -49,6 +50,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
@@ -253,6 +255,11 @@ class StorageManagerService extends IStorageManager.Stub
public void onCleanupUser(int userHandle) {
mStorageManagerService.onCleanupUser(userHandle);
}
+
+ @Override
+ public void onStopUser(int userHandle) {
+ mStorageManagerService.onStopUser(userHandle);
+ }
}
private static final boolean DEBUG_EVENTS = false;
@@ -1075,6 +1082,15 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ private void onStopUser(int userId) {
+ Slog.i(TAG, "onStopUser " + userId);
+ try {
+ mStorageSessionController.onUserStopping(userId);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+
private boolean supportsBlockCheckpoint() throws RemoteException {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
return mVold.supportsBlockCheckpoint();
@@ -1309,6 +1325,15 @@ class StorageManagerService extends IStorageManager.Stub
Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
return;
}
+ final ActivityManagerInternal amInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
+
+ if (mIsFuseEnabled && vol.mountUserId >= 0
+ && !amInternal.isUserRunning(vol.mountUserId, 0)) {
+ Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+ + Integer.toString(vol.mountUserId) + " is no longer running.");
+ return;
+ }
if (vol.type == VolumeInfo.TYPE_EMULATED) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
@@ -2229,6 +2254,11 @@ class StorageManagerService extends IStorageManager.Stub
}
private void remountUidExternalStorage(int uid, int mode) {
+ if (uid == Process.SYSTEM_UID) {
+ // No need to remount uid for system because it has all access anyways
+ return;
+ }
+
try {
mVold.remountUid(uid, mode);
} catch (Exception e) {
@@ -3399,7 +3429,13 @@ class StorageManagerService extends IStorageManager.Stub
public void opChanged(int op, int uid, String packageName) throws RemoteException {
if (!ENABLE_ISOLATED_STORAGE) return;
- remountUidExternalStorage(uid, getMountMode(uid, packageName));
+ int mountMode = getMountMode(uid, packageName);
+ boolean isUidActive = LocalServices.getService(ActivityManagerInternal.class)
+ .getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT;
+
+ if (isUidActive) {
+ remountUidExternalStorage(uid, mountMode);
+ }
}
};
@@ -4092,6 +4128,13 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ @Override
+ public void resetUser(int userId) {
+ // TODO(b/145931219): ideally, we only reset storage for the user in question,
+ // but for now, reset everything.
+ mHandler.obtainMessage(H_RESET).sendToTarget();
+ }
+
public boolean hasExternalStorage(int uid, String packageName) {
// No need to check for system uid. This avoids a deadlock between
// PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6105c7422153..305d0fb00c69 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -357,7 +357,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultSubscriptionId());
int newDefaultPhoneId = intent.getIntExtra(
- PhoneConstants.PHONE_KEY,
+ SubscriptionManager.EXTRA_SLOT_INDEX,
SubscriptionManager.getPhoneId(newDefaultSubId));
if (DBG) {
log("onReceive:current mDefaultSubId=" + mDefaultSubId
@@ -2146,6 +2146,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// the legacy intent broadcasting
//
+ // Legacy intent action.
+ /** Fired when a subscription's phone state changes. */
+ private static final String ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED =
+ "android.intent.action.SUBSCRIPTION_PHONE_STATE";
+
+ // Legacy intent extra keys, copied from PhoneConstants.
+ // Used in legacy intents sent here, for backward compatibility.
+ private static final String PHONE_CONSTANTS_SLOT_KEY = "slot";
+ private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription";
+
private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
long ident = Binder.clearCallingIdentity();
try {
@@ -2162,9 +2172,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
state.fillInNotifierBundle(data);
intent.putExtras(data);
// Pass the subscription along with the intent.
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
- intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+ intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -2183,8 +2194,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
Bundle data = new Bundle();
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
- intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+ intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -2219,13 +2230,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// If a valid subId was specified, we should fire off a subId-specific state
// change intent and include the subId.
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.setAction(ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
+ intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
}
// If the phoneId is invalid, the broadcast is for overall call state.
if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
- intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+ intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
}
// Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
@@ -2285,14 +2297,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastDataConnectionFailed(String apnType, int subId) {
Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -2718,5 +2730,4 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return "UNKNOWN";
}
}
-
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index b5cab1f8d62b..e101fe0e192c 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1289,6 +1289,33 @@ public class AccountManagerService
}
protected UserAccounts getUserAccounts(int userId) {
+ try {
+ return getUserAccountsNotChecked(userId);
+ } catch (RuntimeException e) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ // Let it go...
+ throw e;
+ }
+ // User accounts database is corrupted, we must wipe out the whole user, otherwise the
+ // system will crash indefinitely
+ Slog.wtf(TAG, "Removing user " + userId + " due to exception (" + e + ") reading its "
+ + "account database");
+ if (userId == ActivityManager.getCurrentUser() && userId != UserHandle.USER_SYSTEM) {
+ Slog.i(TAG, "Switching to system user first");
+ try {
+ ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Could not switch to " + UserHandle.USER_SYSTEM + ": " + re);
+ }
+ }
+ if (!getUserManager().removeUserEvenWhenDisallowed(userId)) {
+ Slog.e(TAG, "could not remove user " + userId);
+ }
+ throw e;
+ }
+ }
+
+ private UserAccounts getUserAccountsNotChecked(int userId) {
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
boolean validateAccounts = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7f1d5a33f56a..79fe61072a44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2909,6 +2909,12 @@ final class ActivityManagerShellCommand extends ShellCommand {
final PlatformCompat platformCompat = (PlatformCompat)
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
String toggleValue = getNextArgRequired();
+ if (toggleValue.equals("reset-all")) {
+ final String packageName = getNextArgRequired();
+ pw.println("Reset all changes for " + packageName + " to default value.");
+ platformCompat.clearOverrides(packageName);
+ return 0;
+ }
long changeId;
String changeIdString = getNextArgRequired();
try {
@@ -3267,9 +3273,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" without restarting any processes.");
pw.println(" write");
pw.println(" Write all pending state to storage.");
- pw.println(" compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
- pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
- pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+ pw.println(" compat [COMMAND] [...]: sub-commands for toggling app-compat changes.");
+ pw.println(" enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+ pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
+ pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+ pw.println(" reset-all <PACKAGE_NAME>");
+ pw.println(" Removes all existing overrides for all changes for ");
+ pw.println(" <PACKAGE_NAME> (back to default behaviour).");
+ pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6010b1dc88c4..8144a7118096 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -24,6 +24,7 @@ import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioDeviceAddress;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
@@ -400,6 +401,15 @@ import java.io.PrintWriter;
}
}
+ /*package*/ int setPreferredDeviceForStrategySync(int strategy,
+ @NonNull AudioDeviceAddress device) {
+ return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
+ }
+
+ /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
+ return mDeviceInventory.removePreferredDeviceForStrategySync(strategy);
+ }
+
//---------------------------------------------------------------------
// Communication with (to) AudioService
//TODO check whether the AudioService methods are candidates to move here
@@ -533,6 +543,15 @@ import java.io.PrintWriter;
sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
}
+ /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDeviceAddress device)
+ {
+ sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
+ }
+
+ /*package*/ void postSaveRemovePreferredDeviceForStrategy(int strategy) {
+ sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+ }
+
//---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method
@@ -631,6 +650,7 @@ import java.io.PrintWriter;
} else {
pw.println("Message handler is null");
}
+ mDeviceInventory.dump(pw, prefix);
}
//---------------------------------------------------------------------
@@ -890,6 +910,15 @@ import java.io.PrintWriter;
info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
}
} break;
+ case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
+ final int strategy = msg.arg1;
+ final AudioDeviceAddress device = (AudioDeviceAddress) msg.obj;
+ mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
+ } break;
+ case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
+ final int strategy = msg.arg1;
+ mDeviceInventory.onSaveRemovePreferredDevice(strategy);
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -941,6 +970,8 @@ import java.io.PrintWriter;
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
// a ScoClient died in BtHelper
private static final int MSG_L_SCOCLIENT_DIED = 32;
+ private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33;
+ private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;
private static boolean isMessageHandledUnderWakelock(int msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 37add3da5a48..661451b882cc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
+import android.media.AudioDeviceAddress;
import android.media.AudioDevicePort;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -35,6 +36,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -42,6 +44,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -60,6 +63,9 @@ public class AudioDeviceInventory {
// Key for map created from DeviceInfo.makeDeviceListKey()
private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>();
+ // List of preferred devices for strategies
+ private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();
+
private @NonNull AudioDeviceBroker mDeviceBroker;
// cache of the address of the last dock the device was connected to
@@ -140,12 +146,20 @@ public class AudioDeviceInventory {
}
//------------------------------------------------------------
+ /*package*/ void dump(PrintWriter pw, String prefix) {
+ pw.println("\n" + prefix + "Preferred devices for strategy:");
+ mPreferredDevices.forEach((strategy, device) -> {
+ pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); });
+ }
+
+ //------------------------------------------------------------
// Message handling from AudioDeviceBroker
/**
* Restore previously connected devices. Use in case of audio server crash
* (see AudioService.onAudioServerDied() method)
*/
+ // Always executed on AudioDeviceBroker message queue
/*package*/ void onRestoreDevices() {
synchronized (mConnectedDevices) {
for (DeviceInfo di : mConnectedDevices.values()) {
@@ -157,6 +171,11 @@ public class AudioDeviceInventory {
di.mDeviceCodecFormat);
}
}
+
+ synchronized (mPreferredDevices) {
+ mPreferredDevices.forEach((strategy, device) -> {
+ AudioSystem.setPreferredDeviceForStrategy(strategy, device); });
+ }
}
// only public for mocking/spying
@@ -431,9 +450,41 @@ public class AudioDeviceInventory {
"android"); // reconnect
}
}
+
+ /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAddress device) {
+ mPreferredDevices.put(strategy, device);
+ }
+
+ /*package*/ void onSaveRemovePreferredDevice(int strategy) {
+ mPreferredDevices.remove(strategy);
+ }
+
//------------------------------------------------------------
//
+ /*package*/ int setPreferredDeviceForStrategySync(int strategy,
+ @NonNull AudioDeviceAddress device) {
+ final long identity = Binder.clearCallingIdentity();
+ final int status = AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+ Binder.restoreCallingIdentity(identity);
+
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveSetPreferredDeviceForStrategy(strategy, device);
+ }
+ return status;
+ }
+
+ /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
+ final long identity = Binder.clearCallingIdentity();
+ final int status = AudioSystem.removePreferredDeviceForStrategy(strategy);
+ Binder.restoreCallingIdentity(identity);
+
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveRemovePreferredDeviceForStrategy(strategy);
+ }
+ return status;
+ }
+
/**
* Implements the communication with AudioSystem to (dis)connect a device in the native layers
* @param connect true if connection
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4bf1de66b408..1a62eb26580c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -63,6 +63,8 @@ import android.hardware.input.InputManager;
import android.hardware.usb.UsbManager;
import android.hidl.manager.V1_0.IServiceManager;
import android.media.AudioAttributes;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
import android.media.AudioFocusInfo;
import android.media.AudioFocusRequest;
import android.media.AudioFormat;
@@ -1633,6 +1635,60 @@ public class AudioService extends IAudioService.Stub
///////////////////////////////////////////////////////////////////////////
// IPC methods
///////////////////////////////////////////////////////////////////////////
+ /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
+ public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAddress device) {
+ if (device == null) {
+ return AudioSystem.ERROR;
+ }
+ enforceModifyAudioRoutingPermission();
+ final String logString = String.format(
+ "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
+ Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ if (device.getRole() == AudioDeviceAddress.ROLE_INPUT) {
+ Log.e(TAG, "Unsupported input routing in " + logString);
+ return AudioSystem.ERROR;
+ }
+
+ final int status = mDeviceBroker.setPreferredDeviceForStrategySync(strategy, device);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in %s)", status, logString));
+ }
+
+ return status;
+ }
+
+ /** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
+ public int removePreferredDeviceForStrategy(int strategy) {
+ enforceModifyAudioRoutingPermission();
+ final String logString =
+ String.format("removePreferredDeviceForStrategy strat:%d", strategy);
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+
+ final int status = mDeviceBroker.removePreferredDeviceForStrategySync(strategy);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in %s)", status, logString));
+ }
+ return status;
+ }
+
+ /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
+ public AudioDeviceAddress getPreferredDeviceForStrategy(int strategy) {
+ enforceModifyAudioRoutingPermission();
+ AudioDeviceAddress[] devices = new AudioDeviceAddress[1];
+ final long identity = Binder.clearCallingIdentity();
+ final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
+ Binder.restoreCallingIdentity(identity);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
+ status, strategy));
+ return null;
+ } else {
+ return devices[0];
+ }
+ }
+
+
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9f1a6bd15ac3..36332c0ad25c 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -295,6 +295,7 @@ public class BtHelper {
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
+ broadcast = false;
break;
default:
// do not broadcast CONNECTING or invalid state
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 27f11ffa60ce..1b1c54682255 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -31,8 +31,6 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.server.am.BatteryStatsService;
public class DataConnectionStats extends BroadcastReceiver {
@@ -44,7 +42,7 @@ public class DataConnectionStats extends BroadcastReceiver {
private final Handler mListenerHandler;
private final PhoneStateListener mPhoneStateListener;
- private IccCardConstants.State mSimState = IccCardConstants.State.READY;
+ private int mSimState = TelephonyManager.SIM_STATE_READY;
private SignalStrength mSignalStrength;
private ServiceState mServiceState;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
@@ -66,7 +64,7 @@ public class DataConnectionStats extends BroadcastReceiver {
| PhoneStateListener.LISTEN_DATA_ACTIVITY);
IntentFilter filter = new IntentFilter();
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler);
@@ -75,7 +73,7 @@ public class DataConnectionStats extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ if (action.equals(Intent.ACTION_SIM_STATE_CHANGED)) {
updateSimState(intent);
notePhoneDataConnectionState();
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
@@ -88,8 +86,8 @@ public class DataConnectionStats extends BroadcastReceiver {
if (mServiceState == null) {
return;
}
- boolean simReadyOrUnknown = mSimState == IccCardConstants.State.READY
- || mSimState == IccCardConstants.State.UNKNOWN;
+ boolean simReadyOrUnknown = mSimState == TelephonyManager.SIM_STATE_READY
+ || mSimState == TelephonyManager.SIM_STATE_UNKNOWN;
boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
&& hasService()
&& mDataState == TelephonyManager.DATA_CONNECTED;
@@ -105,23 +103,23 @@ public class DataConnectionStats extends BroadcastReceiver {
}
private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- mSimState = IccCardConstants.State.ABSENT;
- } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
- mSimState = IccCardConstants.State.READY;
- } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+ String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
+ if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
+ mSimState = TelephonyManager.SIM_STATE_ABSENT;
+ } else if (Intent.SIM_STATE_READY.equals(stateExtra)) {
+ mSimState = TelephonyManager.SIM_STATE_READY;
+ } else if (Intent.SIM_STATE_LOCKED.equals(stateExtra)) {
final String lockedReason =
- intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
- if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = IccCardConstants.State.PIN_REQUIRED;
- } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = IccCardConstants.State.PUK_REQUIRED;
+ intent.getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON);
+ if (Intent.SIM_LOCKED_ON_PIN.equals(lockedReason)) {
+ mSimState = TelephonyManager.SIM_STATE_PIN_REQUIRED;
+ } else if (Intent.SIM_LOCKED_ON_PUK.equals(lockedReason)) {
+ mSimState = TelephonyManager.SIM_STATE_PUK_REQUIRED;
} else {
- mSimState = IccCardConstants.State.NETWORK_LOCKED;
+ mSimState = TelephonyManager.SIM_STATE_NETWORK_LOCKED;
}
} else {
- mSimState = IccCardConstants.State.UNKNOWN;
+ mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index bb7f86233a40..5e085ca293a4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -291,13 +291,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
*
* <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails,
* the exception is logged but not reported to callers.
+ *
+ * @return the old capabilities of this network.
*/
- public void setNetworkCapabilities(NetworkCapabilities nc) {
+ public synchronized NetworkCapabilities getAndSetNetworkCapabilities(
+ @NonNull final NetworkCapabilities nc) {
+ final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc;
final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) {
nm.notifyNetworkCapabilitiesChanged(nc);
}
+ return oldNc;
}
public ConnectivityService connService() {
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexTypeIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexTypeIdentifier.java
new file mode 100644
index 000000000000..4d3961df6092
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexTypeIdentifier.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.serializer;
+
+import android.annotation.IntDef;
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
+import android.content.integrity.Rule;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** A helper class for identifying the indexing type of a given rule. */
+public class RuleIndexTypeIdentifier {
+
+ static final int NOT_INDEXED = 0;
+ static final int PACKAGE_NAME_INDEXED = 1;
+ static final int APP_CERTIFICATE_INDEXED = 2;
+
+ /** Represents which indexed file the rule should be located. */
+ @IntDef(
+ value = {
+ NOT_INDEXED,
+ PACKAGE_NAME_INDEXED,
+ APP_CERTIFICATE_INDEXED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IndexType {
+ }
+
+ /** Determines the indexing file type that a given rule should be located at. */
+ public static int getIndexType(Rule rule) {
+ if (rule == null) {
+ throw new IllegalArgumentException("Indexing type cannot be determined for null rule.");
+ }
+ return getIndexType(rule.getFormula());
+ }
+
+ private static int getIndexType(Formula formula) {
+ if (formula == null) {
+ throw new IllegalArgumentException(
+ "Indexing type cannot be determined for null formula.");
+ }
+
+ switch (formula.getTag()) {
+ case Formula.COMPOUND_FORMULA_TAG:
+ return getIndexTypeForCompoundFormula((CompoundFormula) formula);
+ case Formula.STRING_ATOMIC_FORMULA_TAG:
+ return getIndexTypeForAtomicStringFormula((AtomicFormula) formula);
+ case Formula.INT_ATOMIC_FORMULA_TAG:
+ case Formula.BOOLEAN_ATOMIC_FORMULA_TAG:
+ // Package name and app certificate related formulas are string atomic formulas.
+ return NOT_INDEXED;
+ default:
+ throw new IllegalArgumentException(
+ String.format("Invalid formula tag type: %s", formula.getTag()));
+ }
+ }
+
+ private static int getIndexTypeForCompoundFormula(CompoundFormula compoundFormula) {
+ int connector = compoundFormula.getConnector();
+ List<Formula> formulas = compoundFormula.getFormulas();
+
+ switch (connector) {
+ case CompoundFormula.NOT:
+ // Having a NOT operator in the indexing messes up the indexing; e.g., deny
+ // installation if app certificate is NOT X (should not be indexed with app cert
+ // X). We will not keep these rules indexed.
+ return NOT_INDEXED;
+ case CompoundFormula.AND:
+ case CompoundFormula.OR:
+ Set<Integer> indexingTypesForAllFormulas =
+ formulas.stream()
+ .map(formula -> getIndexType(formula))
+ .collect(Collectors.toSet());
+ if (indexingTypesForAllFormulas.contains(PACKAGE_NAME_INDEXED)) {
+ return PACKAGE_NAME_INDEXED;
+ } else if (indexingTypesForAllFormulas.contains(APP_CERTIFICATE_INDEXED)) {
+ return APP_CERTIFICATE_INDEXED;
+ } else {
+ return NOT_INDEXED;
+ }
+ default:
+ return NOT_INDEXED;
+ }
+ }
+
+ private static int getIndexTypeForAtomicStringFormula(AtomicFormula atomicFormula) {
+ switch (atomicFormula.getKey()) {
+ case AtomicFormula.PACKAGE_NAME:
+ return PACKAGE_NAME_INDEXED;
+ case AtomicFormula.APP_CERTIFICATE:
+ return APP_CERTIFICATE_INDEXED;
+ default:
+ return NOT_INDEXED;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 557ba54b536d..f913ba337071 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -531,8 +531,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
CarrierConfigManager configManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
- String mccMnc = SubscriptionManager.isValidSubscriptionId(ddSubId)
- ? phone.getSimOperator(ddSubId) : phone.getSimOperator();
+ if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+ phone = phone.createForSubscriptionId(ddSubId);
+ }
+ String mccMnc = phone.getSimOperator();
boolean isKeepLppProfile = false;
if (!TextUtils.isEmpty(mccMnc)) {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
@@ -1913,24 +1915,17 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
String setId = null;
int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+ phone = phone.createForSubscriptionId(ddSubId);
+ }
if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
- if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
- setId = phone.getSubscriberId(ddSubId);
- }
- if (setId == null) {
- setId = phone.getSubscriberId();
- }
+ setId = phone.getSubscriberId();
if (setId != null) {
// This means the framework has the SIM card.
type = AGPS_SETID_TYPE_IMSI;
}
} else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
- if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
- setId = phone.getLine1Number(ddSubId);
- }
- if (setId == null) {
- setId = phone.getLine1Number();
- }
+ setId = phone.getLine1Number();
if (setId != null) {
// This means the framework has the SIM card.
type = AGPS_SETID_TYPE_MSISDN;
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index ea4f9c456856..dd522b95a938 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -637,7 +637,7 @@ class GnssVisibilityControl {
return new Notification.Builder(context, SystemNotificationChannels.NETWORK_ALERTS)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on)
.setWhen(0)
- .setOngoing(true)
+ .setOngoing(false)
.setAutoCancel(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index e05511681ba8..ac3bf9ad5d8a 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -44,6 +45,38 @@ public class OverlayActorEnforcer {
private final VerifyCallback mVerifyCallback;
+ /**
+ * @return nullable actor result with {@link ActorState} failure status
+ */
+ static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
+ Map<String, Map<String, String>> namedActors) {
+ if (namedActors.isEmpty()) {
+ return Pair.create(null, ActorState.NO_NAMED_ACTORS);
+ }
+
+ Uri actorUri = Uri.parse(actorUriString);
+
+ String actorScheme = actorUri.getScheme();
+ List<String> actorPathSegments = actorUri.getPathSegments();
+ if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
+ return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
+ }
+
+ String actorNamespace = actorUri.getAuthority();
+ Map<String, String> namespace = namedActors.get(actorNamespace);
+ if (namespace == null) {
+ return Pair.create(null, ActorState.MISSING_NAMESPACE);
+ }
+
+ String actorName = actorPathSegments.get(0);
+ String packageName = namespace.get(actorName);
+ if (TextUtils.isEmpty(packageName)) {
+ return Pair.create(null, ActorState.MISSING_ACTOR_NAME);
+ }
+
+ return Pair.create(packageName, ActorState.ALLOWED);
+ }
+
public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
mVerifyCallback = verifyCallback;
}
@@ -141,31 +174,14 @@ public class OverlayActorEnforcer {
}
}
- Map<String, ? extends Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
- if (namedActors.isEmpty()) {
- return ActorState.NO_NAMED_ACTORS;
- }
-
- Uri actorUri = Uri.parse(actor);
-
- String actorScheme = actorUri.getScheme();
- List<String> actorPathSegments = actorUri.getPathSegments();
- if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
- return ActorState.INVALID_OVERLAYABLE_ACTOR_NAME;
- }
-
- String actorNamespace = actorUri.getAuthority();
- Map<String, String> namespace = namedActors.get(actorNamespace);
- if (namespace == null) {
- return ActorState.MISSING_NAMESPACE;
- }
-
- String actorName = actorPathSegments.get(0);
- String packageName = namespace.get(actorName);
- if (TextUtils.isEmpty(packageName)) {
- return ActorState.MISSING_ACTOR_NAME;
+ Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
+ Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
+ ActorState actorUriState = actorUriPair.second;
+ if (actorUriState != ActorState.ALLOWED) {
+ return actorUriState;
}
+ String packageName = actorUriPair.first;
PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
if (packageInfo == null) {
return ActorState.MISSING_APP_INFO;
@@ -192,7 +208,7 @@ public class OverlayActorEnforcer {
* For easier logging/debugging, a set of all possible failure/success states when running
* enforcement.
*/
- private enum ActorState {
+ enum ActorState {
ALLOWED,
INVALID_ACTOR,
MISSING_NAMESPACE,
@@ -244,7 +260,7 @@ public class OverlayActorEnforcer {
* value maps actor name to package name
*/
@NonNull
- Map<String, ? extends Map<String, String>> getNamedActors();
+ Map<String, Map<String, String>> getNamedActors();
/**
* @return true if the target package has declared an overlayable
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8b69946873d8..f1947ac15645 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1073,8 +1073,6 @@ public final class OverlayManagerService extends SystemService {
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
- // TODO(b/143096091): Remove PackageInfo cache so that PackageManager is always queried
- // to enforce visibility/other permission checks
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
final boolean useCache) {
if (useCache) {
@@ -1097,18 +1095,12 @@ public final class OverlayManagerService extends SystemService {
@Override
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
- // TODO(b/143096091): Remove clearing calling ID
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- return getPackageInfo(packageName, userId, true);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ return getPackageInfo(packageName, userId, true);
}
@NonNull
@Override
- public Map<String, ? extends Map<String, String>> getNamedActors() {
+ public Map<String, Map<String, String>> getNamedActors() {
return SystemConfig.getInstance().getNamedActors();
}
@@ -1136,57 +1128,45 @@ public final class OverlayManagerService extends SystemService {
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
@Nullable String targetOverlayableName, int userId)
throws IOException {
- // TODO(b/143096091): Remove clearing calling ID
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- PackageInfo packageInfo = getPackageInfo(packageName, userId);
- if (packageInfo == null) {
- throw new IOException("Unable to get target package");
- }
+ PackageInfo packageInfo = getPackageInfo(packageName, userId);
+ if (packageInfo == null) {
+ throw new IOException("Unable to get target package");
+ }
- String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+ String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
- ApkAssets apkAssets = null;
- try {
- apkAssets = ApkAssets.loadFromPath(baseCodePath);
- return apkAssets.getOverlayableInfo(targetOverlayableName);
- } finally {
- if (apkAssets != null) {
- try {
- apkAssets.close();
- } catch (Throwable ignored) {
- }
+ ApkAssets apkAssets = null;
+ try {
+ apkAssets = ApkAssets.loadFromPath(baseCodePath);
+ return apkAssets.getOverlayableInfo(targetOverlayableName);
+ } finally {
+ if (apkAssets != null) {
+ try {
+ apkAssets.close();
+ } catch (Throwable ignored) {
}
}
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
}
}
@Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
throws RemoteException, IOException {
- // TODO(b/143096091): Remove clearing calling ID
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
- userId);
- String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
+ userId);
+ String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
- ApkAssets apkAssets = null;
- try {
- apkAssets = ApkAssets.loadFromPath(baseCodePath);
- return apkAssets.definesOverlayable();
- } finally {
- if (apkAssets != null) {
- try {
- apkAssets.close();
- } catch (Throwable ignored) {
- }
+ ApkAssets apkAssets = null;
+ try {
+ apkAssets = ApkAssets.loadFromPath(baseCodePath);
+ return apkAssets.definesOverlayable();
+ } finally {
+ if (apkAssets != null) {
+ try {
+ apkAssets.close();
+ } catch (Throwable ignored) {
}
}
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
}
}
@@ -1229,16 +1209,10 @@ public final class OverlayManagerService extends SystemService {
@Nullable
@Override
public String[] getPackagesForUid(int uid) {
- // TODO(b/143096091): Remove clearing calling ID
- long callingIdentity = Binder.clearCallingIdentity();
try {
- try {
- return mPackageManager.getPackagesForUid(uid);
- } catch (RemoteException ignored) {
- return null;
- }
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
+ return mPackageManager.getPackagesForUid(uid);
+ } catch (RemoteException ignored) {
+ return null;
}
}
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
new file mode 100644
index 000000000000..8bea119f3490
--- /dev/null
+++ b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
@@ -0,0 +1,375 @@
+/*
+ * 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.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.parsing.AndroidPackage;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.PackageSetting;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Track visibility of a targets and overlays to actors.
+ *
+ * 4 cases to handle:
+ * <ol>
+ * <li>Target adds/changes an overlayable to add a reference to an actor
+ * <ul>
+ * <li>Must expose target to actor</li>
+ * <li>Must expose any overlays that pointed to that overlayable name to the actor</li>
+ * </ul>
+ * </li>
+ * <li>Target removes/changes an overlayable to remove a reference to an actor
+ * <ul>
+ * <li>If this target has no other overlayables referencing the actor, hide the
+ * target</li>
+ * <li>For all overlays targeting this overlayable, if the overlay is only visible to
+ * the actor through this overlayable, hide the overlay</li>
+ * </ul>
+ * </li>
+ * <li>Overlay adds/changes an overlay tag to add a reference to an overlayable name
+ * <ul>
+ * <li>Expose this overlay to the actor defined by the target overlayable</li>
+ * </ul>
+ * </li>
+ * <li>Overlay removes/changes an overlay tag to remove a reference to an overlayable name
+ * <ul>
+ * <li>If this overlay is only visible to an actor through this overlayable name's
+ * target's actor</li>
+ * </ul>
+ * </li>
+ * </ol>
+ *
+ * In this class, the names "actor", "target", and "overlay" all refer to the ID representations.
+ * All other use cases are named appropriate. "actor" is actor name, "target" is target package
+ * name, and "overlay" is overlay package name.
+ */
+public class OverlayReferenceMapper {
+
+ private final Object mLock = new Object();
+
+ /**
+ * Keys are actors, values are maps which map target to a set of overlays targeting it.
+ * The presence of a target in the value map means the actor and targets are connected, even
+ * if the corresponding target's set is empty.
+ * See class comment for specific types.
+ */
+ @GuardedBy("mLock")
+ private final Map<String, Map<String, Set<String>>> mActorToTargetToOverlays = new HashMap<>();
+
+ /**
+ * Keys are actor package names, values are generic package names the actor should be able
+ * to see.
+ */
+ @GuardedBy("mLock")
+ private final Map<String, Set<String>> mActorPkgToPkgs = new HashMap<>();
+
+ @GuardedBy("mLock")
+ private boolean mDeferRebuild;
+
+ @NonNull
+ private final Provider mProvider;
+
+ /**
+ * @param deferRebuild whether or not to defer rebuild calls on add/remove until first get call;
+ * useful during boot when multiple packages are added in rapid succession
+ * and queries in-between are not expected
+ */
+ public OverlayReferenceMapper(boolean deferRebuild, @Nullable Provider provider) {
+ this.mDeferRebuild = deferRebuild;
+ this.mProvider = provider != null ? provider : new Provider() {
+ @Nullable
+ @Override
+ public String getActorPkg(String actor) {
+ Map<String, Map<String, String>> namedActors = SystemConfig.getInstance()
+ .getNamedActors();
+
+ Pair<String, OverlayActorEnforcer.ActorState> actorPair =
+ OverlayActorEnforcer.getPackageNameForActor(actor, namedActors);
+ return actorPair.first;
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
+ String target = pkg.getOverlayTarget();
+ if (TextUtils.isEmpty(target)) {
+ return Collections.emptyMap();
+ }
+
+ String overlayable = pkg.getOverlayTargetName();
+ Map<String, Set<String>> targetToOverlayables = new HashMap<>();
+ Set<String> overlayables = new HashSet<>();
+ overlayables.add(overlayable);
+ targetToOverlayables.put(target, overlayables);
+ return targetToOverlayables;
+ }
+ };
+ }
+
+ /**
+ * @return mapping of actor package to a set of packages it can view
+ */
+ @NonNull
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public Map<String, Set<String>> getActorPkgToPkgs() {
+ return mActorPkgToPkgs;
+ }
+
+ public boolean isValidActor(@NonNull String targetName, @NonNull String actorPackageName) {
+ synchronized (mLock) {
+ assertMapBuilt();
+ Set<String> validSet = mActorPkgToPkgs.get(actorPackageName);
+ return validSet != null && validSet.contains(targetName);
+ }
+ }
+
+ /**
+ * Add a package to be considered for visibility. Currently supports adding as a target and/or
+ * an overlay. Adding an actor is not supported. Those are configured as part of
+ * {@link SystemConfig#getNamedActors()}.
+ *
+ * @param pkg the package to add
+ * @param otherPkgs map of other packages to consider, excluding {@param pkg}
+ */
+ public void addPkg(AndroidPackage pkg, Map<String, AndroidPackage> otherPkgs) {
+ synchronized (mLock) {
+ if (!pkg.getOverlayables().isEmpty()) {
+ addTarget(pkg, otherPkgs);
+ }
+
+ // TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
+ if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
+ addOverlay(pkg, otherPkgs);
+ }
+
+ if (!mDeferRebuild) {
+ rebuild();
+ }
+ }
+ }
+
+ /**
+ * Removes a package to be considered for visibility. Currently supports removing as a target
+ * and/or an overlay. Removing an actor is not supported. Those are staticly configured as part
+ * of {@link SystemConfig#getNamedActors()}.
+ *
+ * @param pkgName name to remove, as was added through {@link #addPkg(AndroidPackage, Map)}
+ */
+ public void removePkg(String pkgName) {
+ synchronized (mLock) {
+ removeTarget(pkgName);
+ removeOverlay(pkgName);
+
+ if (!mDeferRebuild) {
+ rebuild();
+ }
+ }
+ }
+
+ private void removeTarget(String target) {
+ synchronized (mLock) {
+ Iterator<Map<String, Set<String>>> iterator =
+ mActorToTargetToOverlays.values().iterator();
+ while (iterator.hasNext()) {
+ Map<String, Set<String>> next = iterator.next();
+ next.remove(target);
+ if (next.isEmpty()) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Associate an actor with an association of a new target to overlays for that target.
+ *
+ * If a target overlays itself, it will not be associated with itself, as only one half of the
+ * relationship needs to exist for visibility purposes.
+ */
+ private void addTarget(AndroidPackage targetPkg, Map<String, AndroidPackage> otherPkgs) {
+ synchronized (mLock) {
+ String target = targetPkg.getPackageName();
+ removeTarget(target);
+
+ Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
+ for (String overlayable : overlayablesToActors.keySet()) {
+ String actor = overlayablesToActors.get(overlayable);
+ addTargetToMap(actor, target);
+
+ for (AndroidPackage overlayPkg : otherPkgs.values()) {
+ Map<String, Set<String>> targetToOverlayables =
+ mProvider.getTargetToOverlayables(overlayPkg);
+ Set<String> overlayables = targetToOverlayables.get(target);
+ if (CollectionUtils.isEmpty(overlayables)) {
+ continue;
+ }
+
+ if (overlayables.contains(overlayable)) {
+ addOverlayToMap(actor, target, overlayPkg.getPackageName());
+ }
+ }
+ }
+ }
+ }
+
+ private void removeOverlay(String overlay) {
+ synchronized (mLock) {
+ for (Map<String, Set<String>> targetToOverlays : mActorToTargetToOverlays.values()) {
+ for (Set<String> overlays : targetToOverlays.values()) {
+ overlays.remove(overlay);
+ }
+ }
+ }
+ }
+
+ /**
+ * Associate an actor with an association of targets to overlays for a new overlay.
+ *
+ * If an overlay targets itself, it will not be associated with itself, as only one half of the
+ * relationship needs to exist for visibility purposes.
+ */
+ private void addOverlay(AndroidPackage overlayPkg, Map<String, AndroidPackage> otherPkgs) {
+ synchronized (mLock) {
+ String overlay = overlayPkg.getPackageName();
+ removeOverlay(overlay);
+
+ Map<String, Set<String>> targetToOverlayables =
+ mProvider.getTargetToOverlayables(overlayPkg);
+ for (Map.Entry<String, Set<String>> entry : targetToOverlayables.entrySet()) {
+ String target = entry.getKey();
+ Set<String> overlayables = entry.getValue();
+ AndroidPackage targetPkg = otherPkgs.get(target);
+ if (targetPkg == null) {
+ continue;
+ }
+
+ String targetPkgName = targetPkg.getPackageName();
+ Map<String, String> overlayableToActor = targetPkg.getOverlayables();
+ for (String overlayable : overlayables) {
+ String actor = overlayableToActor.get(overlayable);
+ if (TextUtils.isEmpty(actor)) {
+ continue;
+ }
+ addOverlayToMap(actor, targetPkgName, overlay);
+ }
+ }
+ }
+ }
+
+ public void rebuildIfDeferred() {
+ synchronized (mLock) {
+ if (mDeferRebuild) {
+ rebuild();
+ mDeferRebuild = false;
+ }
+ }
+ }
+
+ private void assertMapBuilt() {
+ if (mDeferRebuild) {
+ throw new IllegalStateException("The actor map must be built by calling "
+ + "rebuildIfDeferred before it is queried");
+ }
+ }
+
+ private void rebuild() {
+ synchronized (mLock) {
+ mActorPkgToPkgs.clear();
+ for (String actor : mActorToTargetToOverlays.keySet()) {
+ String actorPkg = mProvider.getActorPkg(actor);
+ if (TextUtils.isEmpty(actorPkg)) {
+ continue;
+ }
+
+ Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+ Set<String> pkgs = new HashSet<>();
+
+ for (String target : targetToOverlays.keySet()) {
+ Set<String> overlays = targetToOverlays.get(target);
+ pkgs.add(target);
+ pkgs.addAll(overlays);
+ }
+
+ mActorPkgToPkgs.put(actorPkg, pkgs);
+ }
+ }
+ }
+
+ private void addTargetToMap(String actor, String target) {
+ Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+ if (targetToOverlays == null) {
+ targetToOverlays = new HashMap<>();
+ mActorToTargetToOverlays.put(actor, targetToOverlays);
+ }
+
+ Set<String> overlays = targetToOverlays.get(target);
+ if (overlays == null) {
+ overlays = new HashSet<>();
+ targetToOverlays.put(target, overlays);
+ }
+ }
+
+ private void addOverlayToMap(String actor, String target, String overlay) {
+ synchronized (mLock) {
+ Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+ if (targetToOverlays == null) {
+ targetToOverlays = new HashMap<>();
+ mActorToTargetToOverlays.put(actor, targetToOverlays);
+ }
+
+ Set<String> overlays = targetToOverlays.get(target);
+ if (overlays == null) {
+ overlays = new HashSet<>();
+ targetToOverlays.put(target, overlays);
+ }
+
+ overlays.add(overlay);
+ }
+ }
+
+ public interface Provider {
+
+ /**
+ * Given the actor string from an overlayable definition, return the actor's package name.
+ */
+ @Nullable
+ String getActorPkg(String actor);
+
+ /**
+ * Mock response of multiple overlay tags.
+ *
+ * TODO(b/119899133): Replace with actual implementation; fix OverlayReferenceMapperTests
+ */
+ @NonNull
+ Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg);
+ }
+}
diff --git a/services/core/java/com/android/server/package-info.java b/services/core/java/com/android/server/package-info.java
new file mode 100644
index 000000000000..a783e8d61a32
--- /dev/null
+++ b/services/core/java/com/android/server/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * 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.annotation.Hide
+package com.android.server;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 8374ee63e07e..c4bcf809a67d 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,8 +16,6 @@
package com.android.server.pm;
-import static android.content.pm.PackageParser.Component;
-import static android.content.pm.PackageParser.IntentInfo;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
@@ -26,7 +24,6 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ComponentParseUtils;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
@@ -50,9 +47,9 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
+import com.android.server.om.OverlayReferenceMapper;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -109,11 +106,16 @@ public class AppsFilter {
private final FeatureConfig mFeatureConfig;
+ private final OverlayReferenceMapper mOverlayReferenceMapper;
+
AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
- boolean systemAppsQueryable) {
+ boolean systemAppsQueryable,
+ @Nullable OverlayReferenceMapper.Provider overlayProvider) {
mFeatureConfig = featureConfig;
mForceQueryableByDevicePackageNames = forceQueryableWhitelist;
mSystemAppsQueryable = systemAppsQueryable;
+ mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
+ overlayProvider);
}
public interface FeatureConfig {
@@ -193,7 +195,7 @@ public class AppsFilter {
}
}
return new AppsFilter(featureConfig, forcedQueryablePackageNames,
- forceSystemAppsQueryable);
+ forceSystemAppsQueryable, null);
}
/** Returns true if the querying package may query for the potential target package */
@@ -282,6 +284,7 @@ public class AppsFilter {
public void onSystemReady() {
mFeatureConfig.onSystemReady();
+ mOverlayReferenceMapper.rebuildIfDeferred();
}
/**
@@ -338,6 +341,16 @@ public class AppsFilter {
}
}
}
+
+ int existingSize = existingSettings.size();
+ ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
+ for (int index = 0; index < existingSize; index++) {
+ PackageSetting pkgSetting = existingSettings.valueAt(index);
+ if (pkgSetting.pkg != null) {
+ existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
+ }
+ }
+ mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -381,6 +394,8 @@ public class AppsFilter {
addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
}
}
+
+ mOverlayReferenceMapper.removePkg(setting.name);
}
/**
@@ -397,8 +412,7 @@ public class AppsFilter {
PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
try {
- if (!shouldFilterApplicationInternal(callingUid, callingSetting,
- targetPkgSetting,
+ if (!shouldFilterApplicationInternal(callingUid, callingSetting, targetPkgSetting,
userId)) {
return false;
}
@@ -412,8 +426,8 @@ public class AppsFilter {
}
}
- private boolean shouldFilterApplicationInternal(int callingUid,
- SettingBase callingSetting, PackageSetting targetPkgSetting, int userId) {
+ private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
+ PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
try {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
@@ -530,6 +544,29 @@ public class AppsFilter {
}
}
}
+
+ if (callingSharedPkgSettings != null) {
+ int size = callingSharedPkgSettings.size();
+ for (int index = 0; index < size; index++) {
+ PackageSetting pkgSetting = callingSharedPkgSettings.valueAt(index);
+ if (mOverlayReferenceMapper.isValidActor(targetName, pkgSetting.name)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting,
+ "matches shared user of package that acts on target of "
+ + "overlay");
+ }
+ return false;
+ }
+ }
+ } else {
+ if (mOverlayReferenceMapper.isValidActor(targetName, callingPkgSetting.name)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "acts on target of overlay");
+ }
+ return false;
+ }
+ }
+
return true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73ab82dc01b2..4711ec00dcad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1603,6 +1603,10 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
+ if (data != null && data.res.freezer != null) {
+ data.res.freezer.close();
+ }
+
if (data != null && data.mPostInstallRunnable != null) {
data.mPostInstallRunnable.run();
} else if (data != null) {
@@ -13434,35 +13438,10 @@ public class PackageManagerService extends IPackageManager.Stub
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (bm != null) {
- // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
- // in the BackupManager. USER_ALL is used in compatibility tests.
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- }
- if (DEBUG_INSTALL) {
- Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
- }
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
- try {
- if (bm.isBackupServiceActive(userId)) {
- bm.restoreAtInstallForUser(
- userId, res.pkg.getAppInfoPackageName(), token);
- } else {
- doRestore = false;
- }
- } catch (RemoteException e) {
- // can't happen; the backup manager is local
- } catch (Exception e) {
- Slog.e(TAG, "Exception trying to enqueue restore", e);
- doRestore = false;
- }
- } else {
- Slog.e(TAG, "Backup Manager not found!");
- doRestore = false;
+ if (res.freezer != null) {
+ res.freezer.close();
}
+ doRestore = performBackupManagerRestore(userId, token, res);
}
// If this is an update to a package that might be potentially downgraded, then we
@@ -13471,42 +13450,7 @@ public class PackageManagerService extends IPackageManager.Stub
//
// TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
- IRollbackManager rm = IRollbackManager.Stub.asInterface(
- ServiceManager.getService(Context.ROLLBACK_SERVICE));
-
- final String packageName = res.pkg.getAppInfoPackageName();
- final String seInfo = res.pkg.getSeInfo();
- final int[] allUsers = mUserManager.getUserIds();
- final int[] installedUsers;
-
- final PackageSetting ps;
- int appId = -1;
- long ceDataInode = -1;
- synchronized (mSettings) {
- ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- appId = ps.appId;
- ceDataInode = ps.getCeDataInode(userId);
- }
-
- // NOTE: We ignore the user specified in the InstallParam because we know this is
- // an update, and hence need to restore data for all installed users.
- installedUsers = ps.queryInstalledUsers(allUsers, true);
- }
-
- boolean doSnapshotOrRestore = data != null && data.args != null
- && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
- || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
-
- if (ps != null && doSnapshotOrRestore) {
- try {
- rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
- seInfo, token);
- } catch (RemoteException re) {
- Log.e(TAG, "Error snapshotting/restoring user data: " + re);
- }
- doRestore = true;
- }
+ doRestore = performRollbackManagerRestore(userId, token, res, data);
}
if (!doRestore) {
@@ -13522,6 +13466,89 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
+ * Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
+ * Returns whether the restore successfully completed.
+ */
+ private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
+ IBackupManager bm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ if (bm != null) {
+ // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
+ // in the BackupManager. USER_ALL is used in compatibility tests.
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
+ if (DEBUG_INSTALL) {
+ Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
+ }
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
+ try {
+ if (bm.isBackupServiceActive(userId)) {
+ bm.restoreAtInstallForUser(
+ userId, res.pkg.getAppInfoPackageName(), token);
+ } else {
+ return false;
+ }
+ } catch (RemoteException e) {
+ // can't happen; the backup manager is local
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception trying to enqueue restore", e);
+ return false;
+ }
+ } else {
+ Slog.e(TAG, "Backup Manager not found!");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
+ * Returns whether the restore successfully completed.
+ */
+ private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
+ PostInstallData data) {
+ IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
+ final String packageName = res.pkg.getAppInfoPackageName();
+ final String seInfo = res.pkg.getSeInfo();
+ final int[] allUsers = mUserManager.getUserIds();
+ final int[] installedUsers;
+
+ final PackageSetting ps;
+ int appId = -1;
+ long ceDataInode = -1;
+ synchronized (mSettings) {
+ ps = mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ appId = ps.appId;
+ ceDataInode = ps.getCeDataInode(userId);
+ }
+
+ // NOTE: We ignore the user specified in the InstallParam because we know this is
+ // an update, and hence need to restore data for all installed users.
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+ }
+
+ boolean doSnapshotOrRestore = data != null && data.args != null
+ && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+ || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
+
+ if (ps != null && doSnapshotOrRestore) {
+ try {
+ rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
+ seInfo, token);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error snapshotting/restoring user data: " + re);
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Callback from PackageSettings whenever an app is first transitioned out of the
* 'stopped' state. Normally we just issue the broadcast, but we can't do that if
* the app was "launched" for a restoreAtInstall operation. Therefore we check
@@ -14802,6 +14829,7 @@ public class PackageManagerService extends IPackageManager.Stub
ArrayMap<String, PackageInstalledInfo> addedChildPackages;
// The set of packages consuming this shared library or null if no consumers exist.
ArrayList<AndroidPackage> libraryConsumers;
+ PackageFreezer freezer;
public void setError(int code, String msg) {
setReturnCode(code);
@@ -15675,16 +15703,12 @@ public class PackageManagerService extends IPackageManager.Stub
// TODO(patb): create a more descriptive reason than unknown in future release
// mark all non-failure installs as UNKNOWN so we do not treat them as success
for (InstallRequest request : requests) {
+ request.installResult.freezer.close();
if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
}
}
}
- for (PrepareResult result : prepareResults.values()) {
- if (result.freezer != null) {
- result.freezer.close();
- }
- }
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@@ -15792,15 +15816,13 @@ public class PackageManagerService extends IPackageManager.Stub
public final ParsedPackage packageToScan;
public final boolean clearCodeCache;
public final boolean system;
- public final PackageFreezer freezer;
public final PackageSetting originalPs;
public final PackageSetting disabledPs;
private PrepareResult(boolean replace, int scanFlags,
int parseFlags, AndroidPackage existingPackage,
ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
- PackageFreezer freezer, PackageSetting originalPs,
- PackageSetting disabledPs) {
+ PackageSetting originalPs, PackageSetting disabledPs) {
this.replace = replace;
this.scanFlags = scanFlags;
this.parseFlags = parseFlags;
@@ -15808,7 +15830,6 @@ public class PackageManagerService extends IPackageManager.Stub
this.packageToScan = packageToScan;
this.clearCodeCache = clearCodeCache;
this.system = system;
- this.freezer = freezer;
this.originalPs = originalPs;
this.disabledPs = disabledPs;
}
@@ -16437,9 +16458,10 @@ public class PackageManagerService extends IPackageManager.Stub
shouldCloseFreezerBeforeReturn = false;
return new PrepareResult(replace, targetScanFlags, targetParseFlags,
- existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg, freezer,
+ existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
ps, disabledPs);
} finally {
+ res.freezer = freezer;
if (shouldCloseFreezerBeforeReturn) {
freezer.close();
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 5f39f51f9a42..b1feb148e5b3 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -677,10 +677,19 @@ public final class PermissionPolicyService extends SystemService {
private void setUidMode(int opCode, int uid, int mode,
@NonNull String packageName) {
- final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
- .opToPublicName(opCode), uid, packageName);
- if (currentMode != mode) {
+ final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+ opCode), uid, packageName);
+ if (oldMode != mode) {
mAppOpsManager.setUidMode(opCode, uid, mode);
+ final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+ opCode), uid, packageName);
+ if (newMode != mode) {
+ // Work around incorrectly-set package mode. It never makes sense for app ops
+ // related to runtime permissions, but can get in the way and we have to reset
+ // it.
+ mAppOpsManager.setMode(opCode, uid, packageName, AppOpsManager.opToDefaultMode(
+ opCode));
+ }
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index a62616623cb5..e53aab3e1d9b 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -103,8 +103,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
@Override
public int onHealthCheckFailed(VersionedPackage failedPackage) {
- if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
- == null) {
+ if (getAvailableRollback(failedPackage) == null) {
// Don't handle the notification, no rollbacks available for the package
return PackageHealthObserverImpact.USER_IMPACT_NONE;
} else {
@@ -115,51 +114,14 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
@Override
public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) {
- RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
- VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
- RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage);
- int reasonToLog = mapFailureReasonToMetric(rollbackReason);
-
+ RollbackInfo rollback = getAvailableRollback(failedPackage);
if (rollback == null) {
Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
+ failedPackage.getPackageName() + "] with versionCode: ["
+ failedPackage.getVersionCode() + "]");
return false;
}
-
- logEvent(moduleMetadataPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
- reasonToLog, failedPackage.getPackageName());
- LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
- int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
- RollbackManager.STATUS_FAILURE);
- if (status == RollbackManager.STATUS_SUCCESS) {
- if (rollback.isStaged()) {
- int rollbackId = rollback.getRollbackId();
- synchronized (mPendingStagedRollbackIds) {
- mPendingStagedRollbackIds.add(rollbackId);
- }
- BroadcastReceiver listener =
- listenForStagedSessionReady(rollbackManager, rollbackId,
- moduleMetadataPackage);
- handleStagedSessionChange(rollbackManager, rollbackId, listener,
- moduleMetadataPackage);
- } else {
- logEvent(moduleMetadataPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
- reasonToLog, failedPackage.getPackageName());
- }
- } else {
- logEvent(moduleMetadataPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
- reasonToLog, failedPackage.getPackageName());
- }
- });
-
- mHandler.post(() ->
- rollbackManager.commitRollback(rollback.getRollbackId(),
- Collections.singletonList(failedPackage),
- rollbackReceiver.getIntentSender()));
+ rollbackPackage(rollback, failedPackage, rollbackReason);
// Assume rollback executed successfully
return true;
}
@@ -188,9 +150,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
String moduleMetadataPackageName = getModuleMetadataPackageName();
- VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();
- if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
+ if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
scheduleCheckAndMitigateNativeCrashes();
}
@@ -242,8 +203,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
}
- private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
- VersionedPackage failedPackage) {
+ private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
boolean hasFailedPackage = packageRollback.getPackageName().equals(
@@ -285,7 +246,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
- int rollbackId, VersionedPackage moduleMetadataPackage) {
+ int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) {
BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -300,7 +261,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
- BroadcastReceiver listener, VersionedPackage moduleMetadataPackage) {
+ BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) {
PackageInstaller packageInstaller =
mContext.getPackageManager().getPackageInstaller();
List<RollbackInfo> recentRollbacks =
@@ -390,7 +351,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
mNumberOfNativeCrashPollsRemaining--;
// Check if native watchdog reported a crash
if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
- execute(getModuleMetadataPackage(), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+ rollbackAll();
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
} else {
@@ -402,6 +363,93 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
/**
+ * Returns true if the package name is the name of a module.
+ */
+ private boolean isModule(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ return pm.getModuleInfo(packageName, 0) != null;
+ } catch (PackageManager.NameNotFoundException ignore) {
+ return false;
+ }
+ }
+
+ private VersionedPackage getVersionedPackage(String packageName) {
+ try {
+ return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
+ packageName, 0 /* flags */).getLongVersionCode());
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Rolls back the session that owns {@code failedPackage}
+ *
+ * @param rollback {@code rollbackInfo} of the {@code failedPackage}
+ * @param failedPackage the package that needs to be rolled back
+ */
+ private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
+ @FailureReasons int rollbackReason) {
+ final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ int reasonToLog = mapFailureReasonToMetric(rollbackReason);
+ final VersionedPackage logPackage = isModule(failedPackage.getPackageName())
+ ? getModuleMetadataPackage()
+ : null;
+
+ logEvent(logPackage,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+ reasonToLog, failedPackage.getPackageName());
+ final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
+ int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_FAILURE);
+ if (status == RollbackManager.STATUS_SUCCESS) {
+ if (rollback.isStaged()) {
+ int rollbackId = rollback.getRollbackId();
+ synchronized (mPendingStagedRollbackIds) {
+ mPendingStagedRollbackIds.add(rollbackId);
+ }
+ BroadcastReceiver listener =
+ listenForStagedSessionReady(rollbackManager, rollbackId,
+ logPackage);
+ handleStagedSessionChange(rollbackManager, rollbackId, listener,
+ logPackage);
+ } else {
+ logEvent(logPackage,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ reasonToLog, failedPackage.getPackageName());
+ }
+ } else {
+ logEvent(logPackage,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ reasonToLog, failedPackage.getPackageName());
+ }
+ });
+
+ mHandler.post(() ->
+ rollbackManager.commitRollback(rollback.getRollbackId(),
+ Collections.singletonList(failedPackage),
+ rollbackReceiver.getIntentSender()));
+ }
+
+ private void rollbackAll() {
+ Slog.i(TAG, "Rolling back all available rollbacks");
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
+
+ for (RollbackInfo rollback : rollbacks) {
+ String samplePackageName = rollback.getPackages().get(0).getPackageName();
+ VersionedPackage sampleVersionedPackage = getVersionedPackage(samplePackageName);
+ if (sampleVersionedPackage == null) {
+ Slog.e(TAG, "Failed to rollback " + samplePackageName);
+ continue;
+ }
+ rollbackPackage(rollback, sampleVersionedPackage,
+ PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+ }
+ }
+
+ /**
* Since this method can eventually trigger a RollbackManager rollback, it should be called
* only once boot has completed {@code onBootCompleted} and not earlier, because the install
* session must be entirely completed before we try to rollback.
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index d76836f90e6b..46dd366f1909 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -43,7 +43,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
-import java.io.IOException;
/**
* Controls storage sessions for users initiated by the {@link StorageManagerService}.
@@ -101,18 +100,10 @@ public final class StorageSessionController {
connection = new StorageUserConnection(mContext, userId, this);
mConnections.put(userId, connection);
}
- Slog.i(TAG, "Creating session with id: " + sessionId);
- connection.createSession(sessionId, new ParcelFileDescriptor(deviceFd),
+ Slog.i(TAG, "Creating and starting session with id: " + sessionId);
+ connection.startSession(sessionId, new ParcelFileDescriptor(deviceFd),
vol.getPath().getPath(), vol.getInternalPath().getPath());
}
-
- // At boot, a volume can be mounted before user is unlocked, in that case, we create it
- // above and save it so that we can restart all sessions when the user is unlocked
- if (mExternalStorageServiceComponent != null) {
- connection.startSession(sessionId);
- } else {
- Slog.i(TAG, "Controller not initialised, session not started " + sessionId);
- }
}
/**
@@ -179,23 +170,32 @@ public final class StorageSessionController {
* a session will be ignored.
*/
public void onUnlockUser(int userId) throws ExternalStorageServiceException {
- if (!shouldHandle(null)) {
- return;
- }
-
Slog.i(TAG, "On user unlock " + userId);
- if (userId == 0) {
+ if (shouldHandle(null) && userId == 0) {
initExternalStorageServiceComponent();
}
+ }
+ /**
+ * Called when a user is in the process is being stopped.
+ *
+ * Does nothing if {@link #shouldHandle} is {@code false}
+ *
+ * This call removes all sessions for the user that is being stopped;
+ * this will make sure that we don't rebind to the service needlessly.
+ */
+ public void onUserStopping(int userId) throws ExternalStorageServiceException {
+ if (!shouldHandle(null)) {
+ return;
+ }
StorageUserConnection connection = null;
synchronized (mLock) {
connection = mConnections.get(userId);
}
if (connection != null) {
- Slog.i(TAG, "Restarting all sessions for user: " + userId);
- connection.startAllSessions();
+ Slog.i(TAG, "Removing all sessions for user: " + userId);
+ connection.removeAllSessions();
} else {
Slog.w(TAG, "No connection found for user: " + userId);
}
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 7c4773033518..5c44eee4c789 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
import android.service.storage.ExternalStorageService;
import android.service.storage.IExternalStorageService;
import android.text.TextUtils;
@@ -41,6 +42,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
import java.io.IOException;
import java.util.HashMap;
@@ -73,42 +75,25 @@ public final class StorageUserConnection {
}
/**
- * Creates and stores a storage {@link Session}.
+ * Creates and starts a storage {@link Session}.
*
* They must also be cleaned up with {@link #removeSession}.
*
* @throws IllegalArgumentException if a {@code Session} with {@code sessionId} already exists
*/
- public void createSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
- String lowerPath) {
+ public void startSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
+ String lowerPath) throws ExternalStorageServiceException {
Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(pfd);
Preconditions.checkNotNull(upperPath);
Preconditions.checkNotNull(lowerPath);
- synchronized (mLock) {
- Preconditions.checkArgument(!mSessions.containsKey(sessionId));
- mSessions.put(sessionId, new Session(sessionId, pfd, upperPath, lowerPath));
- }
- }
-
- /**
- * Starts an already created storage {@link Session} for {@code sessionId}.
- *
- * It is safe to call this multiple times, however if the session is already started,
- * subsequent calls will be ignored.
- *
- * @throws ExternalStorageServiceException if the session failed to start
- **/
- public void startSession(String sessionId) throws ExternalStorageServiceException {
- Session session;
- synchronized (mLock) {
- session = mSessions.get(sessionId);
- }
-
prepareRemote();
synchronized (mLock) {
- mActiveConnection.startSessionLocked(session);
+ Preconditions.checkArgument(!mSessions.containsKey(sessionId));
+ Session session = new Session(sessionId, upperPath, lowerPath);
+ mSessions.put(sessionId, session);
+ mActiveConnection.startSessionLocked(session, pfd);
}
}
@@ -121,16 +106,10 @@ public final class StorageUserConnection {
**/
public Session removeSession(String sessionId) {
synchronized (mLock) {
- Session session = mSessions.remove(sessionId);
- if (session != null) {
- session.close();
- return session;
- }
- return null;
+ return mSessions.remove(sessionId);
}
}
-
/**
* Removes a session and waits for exit
*
@@ -150,25 +129,29 @@ public final class StorageUserConnection {
}
}
- /** Starts all available sessions for a user without blocking. Any failures will be ignored. */
- public void startAllSessions() {
- try {
- prepareRemote();
- } catch (ExternalStorageServiceException e) {
- Slog.e(TAG, "Failed to start all sessions for user: " + mUserId, e);
- return;
+ /** Restarts all available sessions for a user without blocking.
+ *
+ * Any failures will be ignored.
+ **/
+ public void resetUserSessions() {
+ synchronized (mLock) {
+ if (mSessions.isEmpty()) {
+ // Nothing to reset if we have no sessions to restart; we typically
+ // hit this path if the user was consciously shut down.
+ return;
+ }
}
+ StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
+ sm.resetUser(mUserId);
+ }
+ /**
+ * Removes all sessions, without waiting.
+ */
+ public void removeAllSessions() {
synchronized (mLock) {
- Slog.i(TAG, "Starting " + mSessions.size() + " sessions for user: " + mUserId + "...");
- for (Session session : mSessions.values()) {
- try {
- mActiveConnection.startSessionLocked(session);
- } catch (IllegalStateException | ExternalStorageServiceException e) {
- // TODO: Don't crash process? We could get into process crash loop
- Slog.e(TAG, "Failed to start " + session, e);
- }
- }
+ Slog.i(TAG, "Removing " + mSessions.size() + " sessions for user: " + mUserId + "...");
+ mSessions.clear();
}
}
@@ -269,26 +252,37 @@ public final class StorageUserConnection {
return true;
}
- public void startSessionLocked(Session session) throws ExternalStorageServiceException {
+ public void startSessionLocked(Session session, ParcelFileDescriptor fd)
+ throws ExternalStorageServiceException {
if (!isActiveLocked(session)) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ // ignore
+ }
return;
}
CountDownLatch latch = new CountDownLatch(1);
- try (ParcelFileDescriptor dupedPfd = session.pfd.dup()) {
+ try {
mRemote.startSession(session.sessionId,
FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
- dupedPfd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
+ fd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
setResultLocked(latch, result)));
waitForLatch(latch, "start_session " + session);
maybeThrowExceptionLocked();
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to start session: " + session, e);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ // Ignore
+ }
}
}
public void endSessionLocked(Session session) throws ExternalStorageServiceException {
- session.close();
if (!isActiveLocked(session)) {
// Nothing to end, not started yet
return;
@@ -390,7 +384,7 @@ public final class StorageUserConnection {
// will be called for any required mounts.
// Notify StorageManagerService so it can restart all necessary sessions
close();
- new Thread(StorageUserConnection.this::startAllSessions).start();
+ resetUserSessions();
}
};
}
@@ -411,29 +405,18 @@ public final class StorageUserConnection {
}
}
- private static final class Session implements AutoCloseable {
+ private static final class Session {
public final String sessionId;
- public final ParcelFileDescriptor pfd;
public final String lowerPath;
public final String upperPath;
- Session(String sessionId, ParcelFileDescriptor pfd, String upperPath, String lowerPath) {
+ Session(String sessionId, String upperPath, String lowerPath) {
this.sessionId = sessionId;
- this.pfd = pfd;
this.upperPath = upperPath;
this.lowerPath = lowerPath;
}
@Override
- public void close() {
- try {
- pfd.close();
- } catch (IOException e) {
- Slog.i(TAG, "Failed to close session: " + this);
- }
- }
-
- @Override
public String toString() {
return "[SessionId: " + sessionId + ". UpperPath: " + upperPath + ". LowerPath: "
+ lowerPath + "]";
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 340fe3dbaa97..4e8ba078a73c 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -66,7 +66,8 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
// A log for changes made to the system clock and why.
- @NonNull private final LocalLog mTimeChangesLog = new LocalLog(30);
+ @NonNull
+ private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
// @NonNull after initialize()
private Callback mCallback;
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index e034ad437555..adf6d7e51f4f 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -20,13 +20,9 @@ import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
-import com.android.internal.telephony.TelephonyIntents;
-
/**
* The real implementation of {@link TimeZoneDetectorStrategy.Callback}.
*/
@@ -66,16 +62,8 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat
}
@Override
- public void setDeviceTimeZone(String zoneId, boolean sendNetworkBroadcast) {
+ public void setDeviceTimeZone(String zoneId) {
AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
alarmManager.setTimeZone(zoneId);
-
- if (sendNetworkBroadcast) {
- // TODO Nothing in the platform appears to listen for this. Remove it.
- Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time-zone", zoneId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 5db12c7ac872..b4d80531be54 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -86,7 +86,7 @@ public class TimeZoneDetectorStrategy {
/**
* Sets the device's time zone.
*/
- void setDeviceTimeZone(@NonNull String zoneId, boolean sendNetworkBroadcast);
+ void setDeviceTimeZone(@NonNull String zoneId);
}
private static final String LOG_TAG = "TimeZoneDetectorStrategy";
@@ -172,7 +172,7 @@ public class TimeZoneDetectorStrategy {
* (for use during debugging).
*/
@NonNull
- private final LocalLog mTimeZoneChangesLog = new LocalLog(30);
+ private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
/**
* A mapping from phoneId to a linked list of phone time zone suggestions (the head being the
@@ -333,7 +333,6 @@ public class TimeZoneDetectorStrategy {
Objects.requireNonNull(newZoneId);
Objects.requireNonNull(cause);
- boolean sendNetworkBroadcast = (origin == ORIGIN_PHONE);
boolean isOriginAutomatic = isOriginAutomatic(origin);
if (isOriginAutomatic) {
if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
@@ -373,12 +372,11 @@ public class TimeZoneDetectorStrategy {
return;
}
- mCallback.setDeviceTimeZone(newZoneId, sendNetworkBroadcast);
+ mCallback.setDeviceTimeZone(newZoneId);
String msg = "Set device time zone."
+ " origin=" + origin
+ ", currentZoneId=" + currentZoneId
+ ", newZoneId=" + newZoneId
- + ", sendNetworkBroadcast" + sendNetworkBroadcast
+ ", cause=" + cause;
if (DBG) {
Slog.d(LOG_TAG, msg);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 83c854bae4b5..25079ec758b3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6138,10 +6138,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mCompatDisplayInsets == null || !shouldUseSizeCompatMode()) {
return false;
}
- final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
- if (resolvedAppBounds == null) {
- // The override configuration has not been resolved yet.
+ final Rect appBounds = getConfiguration().windowConfiguration.getAppBounds();
+ if (appBounds == null) {
+ // The app bounds hasn't been computed yet.
return false;
}
@@ -6149,13 +6148,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
// fields should be changed with density and bounds, so here only compares the most
// significant field.
- if (parentConfig.densityDpi != resolvedConfig.densityDpi) {
+ if (parentConfig.densityDpi != getConfiguration().densityDpi) {
return true;
}
final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
- final int appWidth = resolvedAppBounds.width();
- final int appHeight = resolvedAppBounds.height();
+ final int appWidth = appBounds.width();
+ final int appHeight = appBounds.height();
final int parentAppWidth = parentAppBounds.width();
final int parentAppHeight = parentAppBounds.height();
if (parentAppWidth == appWidth && parentAppHeight == appHeight) {
@@ -6174,7 +6173,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The rest of the condition is that only one side is smaller than the parent, but it still
// needs to exclude the cases where the size is limited by the fixed aspect ratio.
if (info.maxAspectRatio > 0) {
- final float aspectRatio = Math.max(appWidth, appHeight) / Math.min(appWidth, appHeight);
+ final float aspectRatio =
+ (float) Math.max(appWidth, appHeight) / Math.min(appWidth, appHeight);
if (aspectRatio >= info.maxAspectRatio) {
// The current size has reached the max aspect ratio.
return false;
@@ -6526,7 +6526,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else if (mCompatDisplayInsets != null) {
// The override changes can only be obtained from display, because we don't have the
// difference of full configuration in each hierarchy.
- final int displayChanges = display.getLastOverrideConfigurationChanges();
+ final int displayChanges = display.getCurrentOverrideConfigurationChanges();
final int orientationChanges = CONFIG_WINDOW_CONFIGURATION
| CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION;
final boolean hasNonOrienSizeChanged = hasResizeChange(displayChanges)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index bfa72e0eff78..bd0ea3d06853 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -81,9 +81,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
*/
private Configuration mFullConfiguration = new Configuration();
- /** The bit mask of the last override fields of full configuration. */
- private int mLastOverrideConfigurationChanges;
-
/**
* Contains merged override configuration settings from the top of the hierarchy down to this
* particular instance. It is different from {@link #mFullConfiguration} because it starts from
@@ -121,11 +118,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return mFullConfiguration;
}
- /** Returns the last changes from applying override configuration. */
- int getLastOverrideConfigurationChanges() {
- return mLastOverrideConfigurationChanges;
- }
-
/**
* Notify that parent config changed and we need to update full configuration.
* @see #mFullConfiguration
@@ -141,8 +133,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
- mLastOverrideConfigurationChanges =
- mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+ mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
onMergedOverrideConfigurationChanged();
// This depends on the assumption that change-listeners don't do
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8e126b56a736..374b72202e29 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -346,6 +346,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** The desired scaling factor for compatible apps. */
float mCompatibleScreenScale;
+ /** @see #getCurrentOverrideConfigurationChanges */
+ private int mCurrentOverrideConfigurationChanges;
+
/**
* Orientation forced by some window. If there is no visible window that specifies orientation
* it is set to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}.
@@ -1867,6 +1870,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTaskStackContainers.onStackWindowingModeChanged(stack);
}
+ /**
+ * The value is only valid in the scope {@link #onRequestedOverrideConfigurationChanged} of the
+ * changing hierarchy and the {@link #onConfigurationChanged} of its children.
+ *
+ * @return The current changes ({@link android.content.pm.ActivityInfo.Config}) of requested
+ * override configuration.
+ */
+ int getCurrentOverrideConfigurationChanges() {
+ return mCurrentOverrideConfigurationChanges;
+ }
+
+ @Override
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ mCurrentOverrideConfigurationChanges =
+ getRequestedOverrideConfiguration().diff(overrideConfiguration);
+ super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+ mCurrentOverrideConfigurationChanges = 0;
+ }
+
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int lastOrientation = getConfiguration().orientation;
diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java
index 0f41302c0b15..0f669817f52e 100644
--- a/services/net/java/android/net/NetworkMonitorManager.java
+++ b/services/net/java/android/net/NetworkMonitorManager.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Hide;
import android.annotation.NonNull;
import android.os.Binder;
import android.os.RemoteException;
@@ -33,6 +34,7 @@ import android.util.Log;
* wrapper methods in this class return a boolean that callers can use to determine whether
* RemoteException was thrown.
*/
+@Hide
public class NetworkMonitorManager {
@NonNull private final INetworkMonitor mNetworkMonitor;
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index 4b7ed3c7b72f..09e333ee3471 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -16,6 +16,7 @@
package android.net.ip;
+import android.annotation.Hide;
import android.annotation.NonNull;
import android.net.NattKeepalivePacketData;
import android.net.ProxyInfo;
@@ -38,6 +39,7 @@ import android.util.Log;
* wrapper methods in this class return a boolean that callers can use to determine whether
* RemoteException was thrown.
*/
+@Hide
public class IpClientManager {
@NonNull private final IIpClient mIpClient;
@NonNull private final String mTag;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index d70e1648f719..96d9c476bcde 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -73,7 +73,7 @@ public class AccessibilityUserStateTest {
@Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener;
- @Mock private Context mContext;
+ @Mock private Context mMockContext;
private MockContentResolver mMockResolver;
@@ -85,11 +85,11 @@ public class AccessibilityUserStateTest {
FakeSettingsProvider.clearSettingsProvider();
mMockResolver = new MockContentResolver();
mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
- mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener);
+ mUserState = new AccessibilityUserState(USER_ID, mMockContext, mMockListener);
}
@After
@@ -109,11 +109,11 @@ public class AccessibilityUserStateTest {
mUserState.setInteractiveUiTimeoutLocked(30);
mUserState.mEnabledServices.add(COMPONENT_NAME);
mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
+ mUserState.mAccessibilityShortcutKeyTargets.add(COMPONENT_NAME.flattenToString());
+ mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
mUserState.setDisplayMagnificationEnabledLocked(true);
mUserState.setNavBarMagnificationEnabledLocked(true);
- mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME);
- mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
mUserState.setAutoclickEnabledLocked(true);
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -128,11 +128,11 @@ public class AccessibilityUserStateTest {
assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
assertTrue(mUserState.mEnabledServices.isEmpty());
assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
+ assertTrue(mUserState.mAccessibilityShortcutKeyTargets.isEmpty());
+ assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
- assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked());
- assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked());
assertFalse(mUserState.isAutoclickEnabledLocked());
assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
@@ -287,6 +287,19 @@ public class AccessibilityUserStateTest {
verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
}
+ @Test
+ public void isShortcutTargetInstalledLocked_returnTrue() {
+ mUserState.mInstalledServices.add(mMockServiceInfo);
+ assertTrue(mUserState.isShortcutTargetInstalledLocked(COMPONENT_NAME.flattenToString()));
+ }
+
+ @Test
+ public void isShortcutTargetInstalledLocked_invalidTarget_returnFalse() {
+ final ComponentName invalidTarget =
+ new ComponentName("com.android.server.accessibility", "InvalidTarget");
+ assertFalse(mUserState.isShortcutTargetInstalledLocked(invalidTarget.flattenToString()));
+ }
+
private int getSecureIntForUser(String key, int userId) {
return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexTypeIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexTypeIdentifierTest.java
new file mode 100644
index 000000000000..18b91b0b1009
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexTypeIdentifierTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.integrity.serializer;
+
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
+import android.content.integrity.Rule;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+/** Unit tests for {@link RuleIndexTypeIdentifier}. */
+@RunWith(JUnit4.class)
+public class RuleIndexTypeIdentifierTest {
+
+ @Test
+ public void getIndexType_nullRule() {
+ Rule rule = null;
+
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex= */
+ "Indexing type cannot be determined for null rule.",
+ () -> RuleIndexTypeIdentifier.getIndexType(rule));
+ }
+
+ @Test
+ public void getIndexType_invalidFormula() {
+ Rule rule = new Rule(getInvalidFormula(), Rule.DENY);
+
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex= */ "Invalid formula tag type.",
+ () -> RuleIndexTypeIdentifier.getIndexType(rule));
+ }
+
+ @Test
+ public void getIndexType_ruleContainingPackageNameFormula() {
+ String packageName = "com.test.app";
+ String installerName = "com.test.installer";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_NAME,
+ installerName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ assertThat(RuleIndexTypeIdentifier.getIndexType(rule))
+ .isEqualTo(RuleIndexTypeIdentifier.PACKAGE_NAME_INDEXED);
+ }
+
+ @Test
+ public void getIndexType_ruleContainingAppCertificateFormula() {
+ String appCertificate = "cert1";
+ String installerName = "com.test.installer";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_NAME,
+ installerName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ assertThat(RuleIndexTypeIdentifier.getIndexType(rule))
+ .isEqualTo(RuleIndexTypeIdentifier.APP_CERTIFICATE_INDEXED);
+ }
+
+ @Test
+ public void getIndexType_ruleWithUnindexedCompoundFormula() {
+ String installerCertificate = "cert1";
+ String installerName = "com.test.installer";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_CERTIFICATE,
+ installerCertificate,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_NAME,
+ installerName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ assertThat(RuleIndexTypeIdentifier.getIndexType(rule))
+ .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED);
+ }
+
+ @Test
+ public void getIndexType_rulContainingCompoundFormulaWithIntAndBoolean() {
+ int appVersion = 12;
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED,
+ /* booleanValue= */ true),
+ new AtomicFormula.IntAtomicFormula(
+ AtomicFormula.VERSION_CODE,
+ AtomicFormula.EQ,
+ appVersion))),
+ Rule.DENY);
+
+ assertThat(RuleIndexTypeIdentifier.getIndexType(rule))
+ .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED);
+ }
+
+ @Test
+ public void getIndexType_negatedRuleContainingPackageNameFormula() {
+ String packageName = "com.test.app";
+ String installerName = "com.test.installer";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Arrays.asList(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_NAME,
+ installerName,
+ /* isHashedValue= */ false))))),
+ Rule.DENY);
+
+ assertThat(RuleIndexTypeIdentifier.getIndexType(rule))
+ .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED);
+ }
+
+ private Formula getInvalidFormula() {
+ return new Formula() {
+ @Override
+ public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
+ return false;
+ }
+
+ @Override
+ public int getTag() {
+ return 4;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ @NonNull
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ }
+ };
+ }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
new file mode 100644
index 000000000000..f45316fc74cd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
@@ -0,0 +1,208 @@
+/*
+ * 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.om
+
+import android.content.pm.parsing.AndroidPackage
+import android.net.Uri
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.testng.Assert.assertThrows
+import test.util.mockThrowOnUnmocked
+import test.util.whenever
+
+@RunWith(Parameterized::class)
+class OverlayReferenceMapperTests {
+
+ companion object {
+ private const val TARGET_PACKAGE_NAME = "com.test.target"
+ private const val OVERLAY_PACKAGE_NAME = "com.test.overlay"
+ private const val ACTOR_PACKAGE_NAME = "com.test.actor"
+ private const val ACTOR_NAME = "overlay://test/actorName"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "deferRebuild {0}")
+ fun parameters() = arrayOf(true, false)
+ }
+
+ private lateinit var mapper: OverlayReferenceMapper
+
+ @JvmField
+ @Parameterized.Parameter(0)
+ var deferRebuild = false
+
+ @Before
+ fun initMapper() {
+ mapper = mapper()
+ }
+
+ @Test
+ fun targetWithOverlay() {
+ val target = mockTarget()
+ val overlay = mockOverlay()
+ val existing = mapper.addInOrder(overlay)
+ assertEmpty()
+ mapper.addInOrder(target, existing = existing)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
+ mapper.remove(target)
+ assertEmpty()
+ }
+
+ @Test
+ fun targetWithMultipleOverlays() {
+ val target = mockTarget()
+ val overlay0 = mockOverlay(0)
+ val overlay1 = mockOverlay(1)
+ mapper = mapper(
+ overlayToTargetToOverlayables = mapOf(
+ overlay0.packageName to mapOf(
+ target.packageName to target.overlayables.keys
+ ),
+ overlay1.packageName to mapOf(
+ target.packageName to target.overlayables.keys
+ )
+ )
+ )
+ val existing = mapper.addInOrder(overlay0, overlay1)
+ assertEmpty()
+ mapper.addInOrder(target, existing = existing)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay0, overlay1))
+ mapper.remove(overlay0)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay1))
+ mapper.remove(target)
+ assertEmpty()
+ }
+
+ @Test
+ fun targetWithoutOverlay() {
+ val target = mockTarget()
+ mapper.addInOrder(target)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+ mapper.remove(target)
+ assertEmpty()
+ }
+
+ @Test
+ fun overlayWithTarget() {
+ val target = mockTarget()
+ val overlay = mockOverlay()
+ val existing = mapper.addInOrder(target)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+ mapper.addInOrder(overlay, existing = existing)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
+ mapper.remove(overlay)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+ }
+
+ @Test
+ fun overlayWithMultipleTargets() {
+ val target0 = mockTarget(0)
+ val target1 = mockTarget(1)
+ val overlay = mockOverlay()
+ mapper = mapper(
+ overlayToTargetToOverlayables = mapOf(
+ overlay.packageName to mapOf(
+ target0.packageName to target0.overlayables.keys,
+ target1.packageName to target1.overlayables.keys
+ )
+ )
+ )
+ mapper.addInOrder(target0, target1, overlay)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
+ mapper.remove(target0)
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
+ mapper.remove(target1)
+ assertEmpty()
+ }
+
+ @Test
+ fun overlayWithoutTarget() {
+ val overlay = mockOverlay()
+ mapper.addInOrder(overlay)
+ // An overlay can only have visibility exposed through its target
+ assertEmpty()
+ mapper.remove(overlay)
+ assertEmpty()
+ }
+
+ private fun OverlayReferenceMapper.addInOrder(
+ vararg pkgs: AndroidPackage,
+ existing: MutableMap<String, AndroidPackage> = mutableMapOf()
+ ) = pkgs.fold(existing) { map, pkg ->
+ addPkg(pkg, map)
+ map[pkg.packageName] = pkg
+ return@fold map
+ }
+
+ private fun OverlayReferenceMapper.remove(pkg: AndroidPackage) = removePkg(pkg.packageName)
+
+ private fun assertMapping(vararg pairs: Pair<String, Set<AndroidPackage>>) {
+ val expected = pairs.associate { it }
+ .mapValues { pair -> pair.value.map { it.packageName }.toSet() }
+
+ // This validates the API exposed for querying the relationships
+ expected.forEach { (actorPkgName, expectedPkgNames) ->
+ expectedPkgNames.forEach { expectedPkgName ->
+ if (deferRebuild) {
+ assertThrows(IllegalStateException::class.java) {
+ mapper.isValidActor(expectedPkgName, actorPkgName)
+ }
+ mapper.rebuildIfDeferred()
+ deferRebuild = false
+ }
+
+ assertThat(mapper.isValidActor(expectedPkgName, actorPkgName)).isTrue()
+ }
+ }
+
+ // This asserts no other relationships are defined besides those tested above
+ assertThat(mapper.actorPkgToPkgs).containsExactlyEntriesIn(expected)
+ }
+
+ private fun assertEmpty() = assertMapping()
+
+ private fun mapper(
+ namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run {
+ mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME))
+ },
+ overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf(
+ mockOverlay().packageName to mapOf(
+ mockTarget().run { packageName to overlayables.keys }
+ )
+ )
+ ) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider {
+ override fun getActorPkg(actor: String?) =
+ OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first
+
+ override fun getTargetToOverlayables(pkg: AndroidPackage) =
+ overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap()
+ })
+
+ private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { "$TARGET_PACKAGE_NAME$increment" }
+ whenever(overlayables) { mapOf("overlayableName$increment" to ACTOR_NAME) }
+ whenever(toString()) { "Package{$packageName}" }
+ }
+
+ private fun mockOverlay(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { "$OVERLAY_PACKAGE_NAME$increment" }
+ whenever(overlayables) { emptyMap<String, String>() }
+ whenever(toString()) { "Package{$packageName}" }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 4fc625a1e1fb..82bbdcba5bc1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -35,6 +35,11 @@ import android.content.pm.parsing.ParsingPackage;
import android.os.Build;
import android.os.Process;
import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.om.OverlayReferenceMapper;
import org.junit.Before;
import org.junit.Test;
@@ -43,11 +48,18 @@ import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
@RunWith(JUnit4.class)
public class AppsFilterTest {
private static final int DUMMY_CALLING_UID = 10345;
private static final int DUMMY_TARGET_UID = 10556;
+ private static final int DUMMY_ACTOR_UID = 10656;
+ private static final int DUMMY_OVERLAY_UID = 10756;
+ private static final int DUMMY_ACTOR_TWO_UID = 10856;
@Mock
AppsFilter.FeatureConfig mFeatureConfigMock;
@@ -117,7 +129,7 @@ public class AppsFilterTest {
@Test
public void testSystemReadyPropogates() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
verify(mFeatureConfigMock).onSystemReady();
}
@@ -125,7 +137,8 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_FilterMatches() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
@@ -138,7 +151,8 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_NoMatchingAction_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -151,7 +165,8 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -169,7 +184,8 @@ public class AppsFilterTest {
@Test
public void testNoQueries_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -182,7 +198,8 @@ public class AppsFilterTest {
@Test
public void testForceQueryable_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
@@ -195,7 +212,8 @@ public class AppsFilterTest {
@Test
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -209,7 +227,8 @@ public class AppsFilterTest {
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -224,7 +243,8 @@ public class AppsFilterTest {
public void testSystemQueryable_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{},
- true /* system force queryable */);
+ true /* system force queryable */, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -238,7 +258,8 @@ public class AppsFilterTest {
@Test
public void testQueriesPackage_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -253,7 +274,8 @@ public class AppsFilterTest {
when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
.thenReturn(false);
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(
appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -266,20 +288,22 @@ public class AppsFilterTest {
@Test
public void testSystemUid_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
- assertFalse(appsFilter.shouldFilterApplication(
- Process.FIRST_APPLICATION_UID - 1, null, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1,
+ null, target, 0));
}
@Test
public void testNonSystemUid_NoCallingSetting_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -290,7 +314,8 @@ public class AppsFilterTest {
@Test
public void testNoTargetPackage_filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
PackageSetting target = new PackageSettingBuilder()
.setName("com.some.package")
@@ -304,6 +329,127 @@ public class AppsFilterTest {
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
+ @Test
+ public void testActsOnTargetOfOverlay() {
+ final String actorName = "overlay://test/actorName";
+
+ ParsingPackage target = pkg("com.some.package.target")
+ .addOverlayable("overlayableName", actorName);
+ ParsingPackage overlay = pkg("com.some.package.overlay")
+ .setIsOverlay(true)
+ .setOverlayTarget(target.getPackageName())
+ .setOverlayTargetName("overlayableName");
+ ParsingPackage actor = pkg("com.some.package.actor");
+
+ final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
+ new OverlayReferenceMapper.Provider() {
+ @Nullable
+ @Override
+ public String getActorPkg(String actorString) {
+ if (actorName.equals(actorString)) {
+ return actor.getPackageName();
+ }
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Set<String>> getTargetToOverlayables(
+ @NonNull AndroidPackage pkg) {
+ if (overlay.getPackageName().equals(pkg.getPackageName())) {
+ Map<String, Set<String>> map = new ArrayMap<>();
+ Set<String> set = new ArraySet<>();
+ set.add(overlay.getOverlayTargetName());
+ map.put(overlay.getOverlayTarget(), set);
+ return map;
+ }
+ return Collections.emptyMap();
+ }
+ });
+ appsFilter.onSystemReady();
+
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
+ PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
+ PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID);
+
+ // Actor can see both target and overlay
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
+ targetSetting, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
+ overlaySetting, 0));
+
+ // But target/overlay can't see each other
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
+ overlaySetting, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
+ targetSetting, 0));
+
+ // And can't see the actor
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
+ actorSetting, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
+ actorSetting, 0));
+ }
+
+ @Test
+ public void testActsOnTargetOfOverlayThroughSharedUser() {
+ final String actorName = "overlay://test/actorName";
+
+ ParsingPackage target = pkg("com.some.package.target")
+ .addOverlayable("overlayableName", actorName);
+ ParsingPackage overlay = pkg("com.some.package.overlay")
+ .setIsOverlay(true)
+ .setOverlayTarget(target.getPackageName())
+ .setOverlayTargetName("overlayableName");
+ ParsingPackage actorOne = pkg("com.some.package.actor.one");
+ ParsingPackage actorTwo = pkg("com.some.package.actor.two");
+
+ final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
+ new OverlayReferenceMapper.Provider() {
+ @Nullable
+ @Override
+ public String getActorPkg(String actorString) {
+ // Only actorOne is mapped as a valid actor
+ if (actorName.equals(actorString)) {
+ return actorOne.getPackageName();
+ }
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Set<String>> getTargetToOverlayables(
+ @NonNull AndroidPackage pkg) {
+ if (overlay.getPackageName().equals(pkg.getPackageName())) {
+ Map<String, Set<String>> map = new ArrayMap<>();
+ Set<String> set = new ArraySet<>();
+ set.add(overlay.getOverlayTargetName());
+ map.put(overlay.getOverlayTarget(), set);
+ return map;
+ }
+ return Collections.emptyMap();
+ }
+ });
+ appsFilter.onSystemReady();
+
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
+ PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
+ PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID);
+ PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo,
+ DUMMY_ACTOR_TWO_UID);
+
+ SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
+ actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags);
+ actorSharedSetting.addPackage(actorOneSetting);
+ actorSharedSetting.addPackage(actorTwoSetting);
+
+ // actorTwo can see both target and overlay
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
+ targetSetting, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
+ overlaySetting, 0));
+ }
+
private interface WithSettingBuilder {
PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 5baeedefef05..2473997a61c9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -22,7 +22,7 @@ import android.util.SparseArray;
import java.io.File;
-class PackageSettingBuilder {
+public class PackageSettingBuilder {
private String mName;
private String mRealName;
private String mCodePath;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
index 270436d65614..cb49fefb8047 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
@@ -50,7 +50,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
-import java.util.Objects;
/**
* White-box unit tests for {@link TimeZoneDetectorStrategy}.
@@ -445,8 +444,7 @@ public class TimeZoneDetectorStrategyTest {
static class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
private boolean mAutoTimeZoneDetectionEnabled;
- private TestState<TimeZoneChange> mTimeZoneChanges = new TestState<>();
- private String mTimeZoneId;
+ private TestState<String> mTimeZoneId = new TestState<>();
@Override
public boolean isAutoTimeZoneDetectionEnabled() {
@@ -460,13 +458,12 @@ public class TimeZoneDetectorStrategyTest {
@Override
public String getDeviceTimeZone() {
- return mTimeZoneId;
+ return mTimeZoneId.getLatest();
}
@Override
- public void setDeviceTimeZone(String zoneId, boolean withNetworkBroadcast) {
- mTimeZoneId = zoneId;
- mTimeZoneChanges.set(new TimeZoneChange(zoneId, withNetworkBroadcast));
+ public void setDeviceTimeZone(String zoneId) {
+ mTimeZoneId.set(zoneId);
}
void initializeAutoTimeZoneDetection(boolean enabled) {
@@ -474,7 +471,7 @@ public class TimeZoneDetectorStrategyTest {
}
void initializeTimeZone(String zoneId) {
- mTimeZoneId = zoneId;
+ mTimeZoneId.init(zoneId);
}
void setAutoTimeZoneDetectionEnabled(boolean enabled) {
@@ -482,46 +479,17 @@ public class TimeZoneDetectorStrategyTest {
}
void assertTimeZoneNotSet() {
- mTimeZoneChanges.assertHasNotBeenSet();
+ mTimeZoneId.assertHasNotBeenSet();
}
- void assertTimeZoneSet(String timeZoneId, boolean withNetworkBroadcast) {
- mTimeZoneChanges.assertHasBeenSet();
- mTimeZoneChanges.assertChangeCount(1);
- TimeZoneChange expectedChange = new TimeZoneChange(timeZoneId, withNetworkBroadcast);
- mTimeZoneChanges.assertLatestEquals(expectedChange);
+ void assertTimeZoneSet(String timeZoneId) {
+ mTimeZoneId.assertHasBeenSet();
+ mTimeZoneId.assertChangeCount(1);
+ mTimeZoneId.assertLatestEquals(timeZoneId);
}
void commitAllChanges() {
- mTimeZoneChanges.commitLatest();
- }
- }
-
- private static class TimeZoneChange {
- private final String mTimeZoneId;
- private final boolean mWithNetworkBroadcast;
-
- private TimeZoneChange(String timeZoneId, boolean withNetworkBroadcast) {
- mTimeZoneId = timeZoneId;
- mWithNetworkBroadcast = withNetworkBroadcast;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- TimeZoneChange that = (TimeZoneChange) o;
- return mWithNetworkBroadcast == that.mWithNetworkBroadcast
- && mTimeZoneId.equals(that.mTimeZoneId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mTimeZoneId, mWithNetworkBroadcast);
+ mTimeZoneId.commitLatest();
}
}
@@ -614,21 +582,13 @@ public class TimeZoneDetectorStrategyTest {
}
Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion suggestion) {
- // Phone suggestions should cause a TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE
- // broadcast.
- boolean withNetworkBroadcast = true;
- mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(
- suggestion.getZoneId(), withNetworkBroadcast);
+ mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(suggestion.getZoneId());
mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
return this;
}
Script verifyTimeZoneSetAndReset(ManualTimeZoneSuggestion suggestion) {
- // Manual suggestions should not cause a TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE
- // broadcast.
- boolean withNetworkBroadcast = false;
- mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(
- suggestion.getZoneId(), withNetworkBroadcast);
+ mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(suggestion.getZoneId());
mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
return this;
}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cdba9a10fa82..cd4f0daa8ebc 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -13,7 +13,7 @@ android_test {
],
static_libs: [
- "frameworks-base-testutils",
+ "frameworks-base-testutils-minus-mockito",
"services.core",
"androidx.test.runner",
"androidx.test.rules",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7166829e01da..dbcfa94c69bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -203,7 +203,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// An activity can be launched on default display.
assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
// An activity cannot be launched on a non-existent display.
- assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
+ assertFalse(activity.canBeLaunchedOnDisplay(Integer.MAX_VALUE));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 212931b76b88..8953cd4cddcf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -27,7 +26,7 @@ import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
@@ -36,7 +35,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -54,7 +52,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
/**
* Tests for Size Compatibility mode.
@@ -265,7 +262,8 @@ public class SizeCompatTests extends ActivityTestsBase {
setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
prepareUnresizable(1.4f /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
- assertTrue(mActivity.inSizeCompatMode());
+ // The display aspect ratio 2.5 > 1.4 (max of activity), so the size is fitted.
+ assertFalse(mActivity.inSizeCompatMode());
final Rect originalBounds = new Rect(mActivity.getBounds());
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
@@ -317,37 +315,35 @@ public class SizeCompatTests extends ActivityTestsBase {
@Test
public void testResetNonVisibleActivity() {
setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+ prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
final ActivityDisplay display = mStack.getDisplay();
- spyOn(display);
+ // Resize the display so the activity is in size compatibility mode.
+ resizeDisplay(display, 900, 1800);
- prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
mActivity.setState(STOPPED, "testSizeCompatMode");
mActivity.mVisibleRequested = false;
mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
- // Make the parent bounds to be different so the activity is in size compatibility mode.
- mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
// Simulate the display changes orientation.
- when(display.getLastOverrideConfigurationChanges()).thenReturn(
- ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
- | ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
- mActivity.onConfigurationChanged(mTask.getConfiguration());
- when(display.getLastOverrideConfigurationChanges()).thenCallRealMethod();
- // The override configuration should not change so it is still in size compatibility mode.
+ final Configuration c = new Configuration();
+ display.getDisplayRotation().setRotation(ROTATION_90);
+ display.computeScreenConfiguration(c);
+ display.onRequestedOverrideConfigurationChanged(c);
+ // Size compatibility mode is able to handle orientation change so the process shouldn't be
+ // restarted and the override configuration won't be cleared.
+ verify(mActivity, never()).restartProcessIfVisible();
assertTrue(mActivity.inSizeCompatMode());
// Change display density
- final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
- displayContent.mBaseDisplayDensity = (int) (0.7f * displayContent.mBaseDisplayDensity);
- final Configuration c = new Configuration();
- displayContent.computeScreenConfiguration(c);
+ display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
+ display.computeScreenConfiguration(c);
mService.mAmInternal = mock(ActivityManagerInternal.class);
- mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+ display.onRequestedOverrideConfigurationChanged(c);
// The override configuration should be reset and the activity's process will be killed.
assertFalse(mActivity.inSizeCompatMode());
verify(mActivity).restartProcessIfVisible();
- mLockRule.runWithScissors(mService.mH, () -> { }, TimeUnit.SECONDS.toMillis(3));
+ waitHandlerIdle(mService.mH);
verify(mService.mAmInternal).killProcess(
eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
}
@@ -362,7 +358,6 @@ public class SizeCompatTests extends ActivityTestsBase {
ActivityRecord activity = mActivity;
activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- ensureActivityConfiguration();
assertFalse(mActivity.inSizeCompatMode());
final ArrayList<IBinder> compatTokens = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index d3b68e02cbf8..a172b656be0c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -287,6 +287,10 @@ public class SystemServicesTestRule implements TestRule {
// Mock root, some default display, and home stack.
spyOn(mWmService.mRoot);
final ActivityDisplay display = mAtmService.mRootActivityContainer.getDefaultDisplay();
+ // Set default display to be in fullscreen mode. Devices with PC feature may start their
+ // default display in freeform mode but some of tests in WmTests have implicit assumption on
+ // that the default display is in fullscreen mode.
+ display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(display);
spyOn(display.mDisplayContent);
final ActivityStack homeStack = display.getStack(
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 1996dd4f6545..ef9a73b794f7 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -229,8 +229,8 @@ public class UsageStatsDatabase {
}
try {
- IntervalStats stats = new IntervalStats();
for (int i = start; i < fileCount - 1; i++) {
+ final IntervalStats stats = new IntervalStats();
readLocked(files.valueAt(i), stats);
if (!checkinAction.checkin(stats)) {
return false;
@@ -848,10 +848,10 @@ public class UsageStatsDatabase {
}
}
- final IntervalStats stats = new IntervalStats();
final ArrayList<T> results = new ArrayList<>();
for (int i = startIndex; i <= endIndex; i++) {
final AtomicFile f = intervalStats.valueAt(i);
+ final IntervalStats stats = new IntervalStats();
if (DEBUG) {
Slog.d(TAG, "Reading stat file " + f.getBaseFile().getAbsolutePath());
@@ -1061,6 +1061,11 @@ public class UsageStatsDatabase {
}
}
+ /**
+ * Note: the data read from the given file will add to the IntervalStats object passed into this
+ * method. It is up to the caller to ensure that this is the desired behavior - if not, the
+ * caller should ensure that the data in the reused object is being cleared.
+ */
private void readLocked(AtomicFile file, IntervalStats statsOut)
throws IOException, RuntimeException {
if (mCurrentVersion <= 3) {
@@ -1072,6 +1077,10 @@ public class UsageStatsDatabase {
/**
* Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
+ * <p/>
+ * Note: the data read from the given file will add to the IntervalStats object passed into this
+ * method. It is up to the caller to ensure that this is the desired behavior - if not, the
+ * caller should ensure that the data in the reused object is being cleared.
*/
private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version,
PackagesTokenData packagesTokenData) throws IOException, RuntimeException {
@@ -1098,6 +1107,10 @@ public class UsageStatsDatabase {
/**
* Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
+ * <p/>
+ * Note: the data read from the given file will add to the IntervalStats object passed into this
+ * method. It is up to the caller to ensure that this is the desired behavior - if not, the
+ * caller should ensure that the data in the reused object is being cleared.
*/
private static boolean readLocked(InputStream in, IntervalStats statsOut, int version,
PackagesTokenData packagesTokenData) throws RuntimeException {
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index f89bbc7d179a..73faf9f5f9f4 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -463,9 +463,7 @@ public class Annotation {
DataFailCause.UNKNOWN,
DataFailCause.RADIO_NOT_AVAILABLE,
DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
- DataFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN,
DataFailCause.LOST_CONNECTION,
- DataFailCause.RESET_BY_FRAMEWORK
})
@Retention(RetentionPolicy.SOURCE)
public @interface DataFailureCause {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2e04730a5ea2..91646add6964 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3913,6 +3913,34 @@ public class CarrierConfigManager {
@SystemApi
@TestApi
public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues) {
+ overrideConfig(subscriptionId, overrideValues, false);
+ }
+
+ /**
+ * Overrides the carrier config of the provided subscription ID with the provided values.
+ *
+ * Any further queries to carrier config from any process will return the overridden values
+ * after this method returns. The overrides are effective until the user passes in {@code null}
+ * for {@code overrideValues}. This removes all previous overrides and sets the carrier config
+ * back to production values.
+ *
+ * The overrides is stored persistently and will survive a reboot if {@code persistent} is true.
+ *
+ * May throw an {@link IllegalArgumentException} if {@code overrideValues} contains invalid
+ * values for the specified config keys.
+ *
+ * NOTE: This API is meant for testing purposes only.
+ *
+ * @param subscriptionId The subscription ID for which the override should be done.
+ * @param overrideValues Key-value pairs of the values that are to be overridden. If set to
+ * {@code null}, this will remove all previous overrides and set the
+ * carrier configuration back to production values.
+ * @param persistent Determines whether the override should be persistent.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues,
+ boolean persistent) {
try {
ICarrierConfigLoader loader = getICarrierConfigLoader();
if (loader == null) {
@@ -3920,7 +3948,7 @@ public class CarrierConfigManager {
+ " ICarrierConfigLoader is null");
return;
}
- loader.overrideConfig(subscriptionId, overrideValues);
+ loader.overrideConfig(subscriptionId, overrideValues, persistent);
} catch (RemoteException ex) {
Rlog.e(TAG, "Error setting config for subId " + subscriptionId + ": "
+ ex.toString());
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 7bdf1f57f8b2..e1c4bef0dd65 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -950,14 +950,10 @@ public final class DataFailCause {
public static final int UNKNOWN = 0x10000;
/** Data fail due to radio not unavailable. */
public static final int RADIO_NOT_AVAILABLE = 0x10001; /* no retry */
- /** @hide */
+ /** Data fail due to unacceptable network parameter. */
public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002; /* no retry */
- /** @hide */
- public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
/** Data connection was lost. */
public static final int LOST_CONNECTION = 0x10004;
- /** @hide */
- public static final int RESET_BY_FRAMEWORK = 0x10005;
/**
* Data handover failed.
@@ -1361,10 +1357,7 @@ public final class DataFailCause {
sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
"UNACCEPTABLE_NETWORK_PARAMETER");
- sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN,
- "CONNECTION_TO_DATACONNECTIONAC_BROKEN");
sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
- sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
}
private DataFailCause() {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index cf705f466e48..a82ae887224c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -48,7 +48,6 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.Process;
@@ -64,6 +63,7 @@ import android.util.Pair;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
import com.android.internal.telephony.ISub;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.util.HandlerExecutor;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 47c468121484..5adc99e11478 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -1173,18 +1173,8 @@ public class ImsCallSession {
public void callSessionMergeComplete(IImsCallSession newSession) {
if (mListener != null) {
if (newSession != null) {
- // Check if the active session is the same session that was
- // active before the merge request was sent.
- ImsCallSession validActiveSession = ImsCallSession.this;
- try {
- if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
- // New session created after conference
- validActiveSession = new ImsCallSession(newSession);
- }
- } catch (RemoteException rex) {
- Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
- }
- mListener.callSessionMergeComplete(validActiveSession);
+ // New session created after conference
+ mListener.callSessionMergeComplete(new ImsCallSession(newSession));
} else {
// Session already exists. Hence no need to pass
mListener.callSessionMergeComplete(null);
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 4fc6a19d1f38..cfc803ca3639 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -17,7 +17,6 @@
package com.android.ims;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.RemoteException;
import android.telephony.Rlog;
@@ -26,6 +25,8 @@ import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
+import com.android.internal.telephony.util.HandlerExecutor;
+
import java.util.concurrent.Executor;
/**
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
index ee09c1c9c62f..76ebc0f3ac2f 100644
--- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -30,7 +30,7 @@ interface ICarrierConfigLoader {
PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
String callingFeatureId);
- void overrideConfig(int subId, in PersistableBundle overrides);
+ void overrideConfig(int subId, in PersistableBundle overrides, boolean persistent);
void notifyConfigChangedForSubId(int subId);
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 6ff27b1152c8..25f03c200610 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.telephony;
+import android.content.Intent;
import android.telephony.TelephonyManager;
import dalvik.annotation.compat.UnsupportedAppUsage;
@@ -25,37 +26,38 @@ import dalvik.annotation.compat.UnsupportedAppUsage;
public class IccCardConstants {
/* The extra data for broadcasting intent INTENT_ICC_STATE_CHANGE */
- public static final String INTENT_KEY_ICC_STATE = "ss";
+ public static final String INTENT_KEY_ICC_STATE = Intent.EXTRA_SIM_STATE;
/* UNKNOWN means the ICC state is unknown */
- public static final String INTENT_VALUE_ICC_UNKNOWN = "UNKNOWN";
+ public static final String INTENT_VALUE_ICC_UNKNOWN = Intent.SIM_STATE_UNKNOWN;
/* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
- public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
+ public static final String INTENT_VALUE_ICC_NOT_READY = Intent.SIM_STATE_NOT_READY;
/* ABSENT means ICC is missing */
- public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+ public static final String INTENT_VALUE_ICC_ABSENT = Intent.SIM_STATE_ABSENT;
/* PRESENT means ICC is present */
- public static final String INTENT_VALUE_ICC_PRESENT = "PRESENT";
+ public static final String INTENT_VALUE_ICC_PRESENT = Intent.SIM_STATE_PRESENT;
/* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
- static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
+ static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = Intent.SIM_STATE_CARD_IO_ERROR;
/* CARD_RESTRICTED means card is present but not usable due to carrier restrictions */
- static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = "CARD_RESTRICTED";
+ static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = Intent.SIM_STATE_CARD_RESTRICTED;
/* LOCKED means ICC is locked by pin or by network */
- public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
+ public static final String INTENT_VALUE_ICC_LOCKED = Intent.SIM_STATE_LOCKED;
/* READY means ICC is ready to access */
- public static final String INTENT_VALUE_ICC_READY = "READY";
+ public static final String INTENT_VALUE_ICC_READY = Intent.SIM_STATE_READY;
/* IMSI means ICC IMSI is ready in property */
- public static final String INTENT_VALUE_ICC_IMSI = "IMSI";
+ public static final String INTENT_VALUE_ICC_IMSI = Intent.SIM_STATE_IMSI;
/* LOADED means all ICC records, including IMSI, are loaded */
- public static final String INTENT_VALUE_ICC_LOADED = "LOADED";
+ public static final String INTENT_VALUE_ICC_LOADED = Intent.SIM_STATE_LOADED;
/* The extra data for broadcasting intent INTENT_ICC_STATE_CHANGE */
- public static final String INTENT_KEY_LOCKED_REASON = "reason";
+ public static final String INTENT_KEY_LOCKED_REASON = Intent.EXTRA_SIM_LOCKED_REASON;
/* PIN means ICC is locked on PIN1 */
- public static final String INTENT_VALUE_LOCKED_ON_PIN = "PIN";
+ public static final String INTENT_VALUE_LOCKED_ON_PIN = Intent.SIM_LOCKED_ON_PIN;
/* PUK means ICC is locked on PUK1 */
- public static final String INTENT_VALUE_LOCKED_ON_PUK = "PUK";
+ public static final String INTENT_VALUE_LOCKED_ON_PUK = Intent.SIM_LOCKED_ON_PUK;
/* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
- public static final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
+ public static final String INTENT_VALUE_LOCKED_NETWORK = Intent.SIM_LOCKED_NETWORK;
/* PERM_DISABLED means ICC is permanently disabled due to puk fails */
- public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+ public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED =
+ Intent.SIM_ABSENT_ON_PERM_DISABLED;
/**
* This is combination of IccCardStatus.CardState and IccCardApplicationStatus.AppState
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index c19ae7b3916a..aad7f3e7bf2a 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -182,10 +182,6 @@ public class PhoneConstants {
public static final String SLOT_KEY = "slot";
- /** Fired when a subscriptions phone state changes. */
- public static final String ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED =
- "android.intent.action.SUBSCRIPTION_PHONE_STATE";
-
// FIXME: This is used to pass a subId via intents, we need to look at its usage, which is
// FIXME: extensive, and see if this should be an array of all active subId's or ...?
/**
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 22168c54510b..9cbcd7fc6b04 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -489,6 +489,8 @@ public interface RILConstants {
int RIL_REQUEST_EMERGENCY_DIAL = 205;
int RIL_REQUEST_GET_PHONE_CAPABILITY = 206;
int RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG = 207;
+ int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208;
+ int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -552,4 +554,5 @@ public interface RILConstants {
int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
+ int RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED = 1103;
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 8b62872b77af..b2c3fc79025b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -240,25 +240,6 @@ public class TelephonyIntents {
*/
public static final String ACTION_NETWORK_SET_TIME = "android.intent.action.NETWORK_SET_TIME";
-
- /**
- * Broadcast Action: The timezone 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-zone</em> - The java.util.TimeZone.getID() value identifying the new time
- * zone.</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.
- */
- public static final String ACTION_NETWORK_SET_TIMEZONE
- = "android.intent.action.NETWORK_SET_TIMEZONE";
-
/**
* <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
* <p class="note">.
diff --git a/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java b/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java
new file mode 100644
index 000000000000..8a2545772b5b
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.internal.telephony.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(@NonNull Handler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+}
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index adcbb4287dd0..9bb9983f66a9 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
java_test_host {
- name: "ApkVerityTests",
+ name: "ApkVerityTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
test_suites: ["general-tests"],
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
index 73779cbd1a87..51bcdea21f49 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -36,6 +36,6 @@
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="ApkVerityTests.jar" />
+ <option name="jar" value="ApkVerityTest.jar" />
</test>
</configuration>
diff --git a/tests/ApkVerityTest/TEST_MAPPING b/tests/ApkVerityTest/TEST_MAPPING
new file mode 100644
index 000000000000..a6608399d83c
--- /dev/null
+++ b/tests/ApkVerityTest/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ // nextgen test only runs during postsubmit.
+ {
+ "name": "ApkVerityTest",
+ "keywords": ["nextgen"]
+ }
+ ],
+ "postsubmit": [
+ // TODO: move to presubmit once it's confirmed stable.
+ {
+ "name": "ApkVerityTest"
+ }
+ ]
+}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 3be7a4a0261d..7b289d8a7858 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -22,18 +22,14 @@ import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoFo
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
-import android.annotation.Nullable;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.ParcelFileDescriptor;
import android.provider.DeviceConfig;
-import android.text.TextUtils;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -44,7 +40,6 @@ import com.android.cts.install.lib.TestApp;
import com.android.cts.install.lib.Uninstall;
import com.android.cts.rollback.lib.Rollback;
import com.android.cts.rollback.lib.RollbackUtils;
-import com.android.internal.R;
import libcore.io.IoUtils;
@@ -75,7 +70,6 @@ public class StagedRollbackTest {
private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
"watchdog_request_timeout_millis";
- private static final String MODULE_META_DATA_PACKAGE = getModuleMetadataPackageName();
private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
getNetworkStackPackageName(), -1, false, findNetworkStackApk());
@@ -186,21 +180,15 @@ public class StagedRollbackTest {
}
/**
- * Stage install ModuleMetadata package to simulate a Mainline module update.
+ * Stage install an apk with rollback that will be later triggered by unattributable crash.
*/
@Test
public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
- resetModuleMetadataPackage();
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- PackageInfo metadataPackageInfo = context.getPackageManager().getPackageInfo(
- MODULE_META_DATA_PACKAGE, 0);
- String metadataApkPath = metadataPackageInfo.applicationInfo.sourceDir;
- assertThat(metadataApkPath).isNotNull();
- assertThat(metadataApkPath).isNotEqualTo("");
-
- runShellCommand("pm install "
- + "-r --enable-rollback --staged --wait "
- + metadataApkPath);
+ Uninstall.packages(TestApp.A);
+ Install.single(TestApp.A1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+ Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
}
/**
@@ -208,9 +196,10 @@ public class StagedRollbackTest {
*/
@Test
public void testNativeWatchdogTriggersRollback_Phase2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- MODULE_META_DATA_PACKAGE)).isNotNull();
+ TestApp.A)).isNotNull();
}
/**
@@ -218,9 +207,10 @@ public class StagedRollbackTest {
*/
@Test
public void testNativeWatchdogTriggersRollback_Phase3() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- MODULE_META_DATA_PACKAGE)).isNotNull();
+ TestApp.A)).isNotNull();
}
@Test
@@ -351,26 +341,6 @@ public class StagedRollbackTest {
getNetworkStackPackageName())).isNull();
}
- @Nullable
- private static String getModuleMetadataPackageName() {
- String packageName = InstrumentationRegistry.getInstrumentation().getContext()
- .getResources().getString(R.string.config_defaultModuleMetadataProvider);
- if (TextUtils.isEmpty(packageName)) {
- return null;
- }
- return packageName;
- }
-
- private void resetModuleMetadataPackage() {
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- assertThat(MODULE_META_DATA_PACKAGE).isNotNull();
- rm.expireRollbackForPackage(MODULE_META_DATA_PACKAGE);
-
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- MODULE_META_DATA_PACKAGE)).isNull();
- }
-
private static void runShellCommand(String cmd) {
ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
.executeShellCommand(cmd);
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index f71be7b0b7d3..e13f906cdccd 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -17,11 +17,24 @@
java_library {
name: "frameworks-base-testutils",
- srcs: ["java/**/*.java"],
+ static_libs: [
+ "frameworks-base-testutils-minus-mockito",
+ "mockito-target-minus-junit4",
+ ]
+}
+
+java_library {
+ name: "frameworks-base-testutils-minus-mockito",
+
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
static_libs: [
"junit",
"hamcrest-library",
+ "truth-prebuilt",
],
libs: [
diff --git a/tests/utils/testutils/java/test/package-info.java b/tests/utils/testutils/java/test/package-info.java
new file mode 100644
index 000000000000..c34d7b21ab84
--- /dev/null
+++ b/tests/utils/testutils/java/test/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package separated from android. because placing classes under android.'s .test/.util
+ * may be confused with tests for that actual android subpackage.
+ **/
+package test;
diff --git a/tests/utils/testutils/java/test/util/MockitoUtils.kt b/tests/utils/testutils/java/test/util/MockitoUtils.kt
new file mode 100644
index 000000000000..5151abe54108
--- /dev/null
+++ b/tests/utils/testutils/java/test/util/MockitoUtils.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package test.util
+
+import org.mockito.Answers
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.stubbing.Answer
+import org.mockito.stubbing.Stubber
+
+object MockitoUtils {
+ val ANSWER_THROWS = Answer<Any?> {
+ when (val name = it.method.name) {
+ "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
+ else -> {
+ val arguments = it.arguments
+ ?.takeUnless { it.isEmpty() }
+ ?.joinToString()
+ ?.let {
+ "with $it"
+ }
+ .orEmpty()
+
+ throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " +
+ "$arguments should not be called")
+ }
+ }
+ }
+}
+
+inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
+
+fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
+fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
+
+@Suppress("UNCHECKED_CAST")
+fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Any?) =
+ Mockito.`when`(mock).thenAnswer { block(it) }
+
+fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
+
+inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
+ val swappingAnswer = object : Answer<Any?> {
+ var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
+
+ override fun answer(invocation: InvocationOnMock?): Any? {
+ return delegate.answer(invocation)
+ }
+ }
+
+ return Mockito.mock(T::class.java, swappingAnswer).apply(block)
+ .also {
+ // To allow when() usage inside block, only swap to throwing afterwards
+ swappingAnswer.delegate = MockitoUtils.ANSWER_THROWS
+ }
+}
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index d45c4e7efb12..712b48edbc1b 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -189,6 +189,7 @@ static int write_java_methods(
}
fprintf(out, "\n");
+ fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
// Add support for writing using Q schema if this is not the default module.
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 86c398b770b7..50d62a0b627e 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1069,7 +1069,7 @@ public class WifiManager {
* @deprecated This API is non-functional and will have no impact.
*/
@Deprecated
- public static final int WIFI_MODE_FULL = WifiProtoEnums.WIFI_MODE_FULL; // 1
+ public static final int WIFI_MODE_FULL = 1;
/**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
@@ -1083,7 +1083,7 @@ public class WifiManager {
* @deprecated This API is non-functional and will have no impact.
*/
@Deprecated
- public static final int WIFI_MODE_SCAN_ONLY = WifiProtoEnums.WIFI_MODE_SCAN_ONLY; // 2
+ public static final int WIFI_MODE_SCAN_ONLY = 2;
/**
* In this Wi-Fi lock mode, Wi-Fi will not go to power save.
@@ -1102,7 +1102,7 @@ public class WifiManager {
* When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF}
* lock will have no impact.
*/
- public static final int WIFI_MODE_FULL_HIGH_PERF = WifiProtoEnums.WIFI_MODE_FULL_HIGH_PERF; // 3
+ public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
/**
* In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency.
@@ -1132,8 +1132,8 @@ public class WifiManager {
* lock will be effective when app is running in foreground and screen is on,
* while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise.
*/
- public static final int WIFI_MODE_FULL_LOW_LATENCY =
- WifiProtoEnums.WIFI_MODE_FULL_LOW_LATENCY; // 4
+ public static final int WIFI_MODE_FULL_LOW_LATENCY = 4;
+
/** Anything worse than or equal to this will show 0 bars. */
@UnsupportedAppUsage
diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
index b07d8edde3d4..61ab92c9416f 100644
--- a/wifi/java/android/net/wifi/aware/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java
@@ -47,6 +47,7 @@ public final class ConfigRequest implements Parcelable {
*/
public static final int NAN_BAND_24GHZ = 0;
public static final int NAN_BAND_5GHZ = 1;
+ public static final int NAN_BAND_6GHZ = 2;
/**
* Magic values for Discovery Window (DW) interval configuration
@@ -60,6 +61,11 @@ public final class ConfigRequest implements Parcelable {
public final boolean mSupport5gBand;
/**
+ * Indicates whether 6G band support is requested.
+ */
+ public final boolean mSupport6gBand;
+
+ /**
* Specifies the desired master preference.
*/
public final int mMasterPreference;
@@ -81,9 +87,10 @@ public final class ConfigRequest implements Parcelable {
*/
public final int mDiscoveryWindowInterval[];
- private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow,
- int clusterHigh, int discoveryWindowInterval[]) {
+ private ConfigRequest(boolean support5gBand, boolean support6gBand, int masterPreference,
+ int clusterLow, int clusterHigh, int[] discoveryWindowInterval) {
mSupport5gBand = support5gBand;
+ mSupport6gBand = support6gBand;
mMasterPreference = masterPreference;
mClusterLow = clusterLow;
mClusterHigh = clusterHigh;
@@ -92,10 +99,12 @@ public final class ConfigRequest implements Parcelable {
@Override
public String toString() {
- return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference="
- + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh="
- + mClusterHigh + ", mDiscoveryWindowInterval="
- + Arrays.toString(mDiscoveryWindowInterval) + "]";
+ return "ConfigRequest [mSupport5gBand=" + mSupport5gBand
+ + ", mSupport6gBand=" + mSupport6gBand
+ + ", mMasterPreference=" + mMasterPreference
+ + ", mClusterLow=" + mClusterLow
+ + ", mClusterHigh=" + mClusterHigh
+ + ", mDiscoveryWindowInterval=" + Arrays.toString(mDiscoveryWindowInterval) + "]";
}
@Override
@@ -106,6 +115,7 @@ public final class ConfigRequest implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mSupport5gBand ? 1 : 0);
+ dest.writeInt(mSupport6gBand ? 1 : 0);
dest.writeInt(mMasterPreference);
dest.writeInt(mClusterLow);
dest.writeInt(mClusterHigh);
@@ -121,13 +131,14 @@ public final class ConfigRequest implements Parcelable {
@Override
public ConfigRequest createFromParcel(Parcel in) {
boolean support5gBand = in.readInt() != 0;
+ boolean support6gBand = in.readInt() != 0;
int masterPreference = in.readInt();
int clusterLow = in.readInt();
int clusterHigh = in.readInt();
int discoveryWindowInterval[] = in.createIntArray();
- return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh,
- discoveryWindowInterval);
+ return new ConfigRequest(support5gBand, support6gBand, masterPreference, clusterLow,
+ clusterHigh, discoveryWindowInterval);
}
};
@@ -143,7 +154,9 @@ public final class ConfigRequest implements Parcelable {
ConfigRequest lhs = (ConfigRequest) o;
- return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
+ return mSupport5gBand == lhs.mSupport5gBand
+ && mSupport6gBand == lhs.mSupport6gBand
+ && mMasterPreference == lhs.mMasterPreference
&& mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh
&& Arrays.equals(mDiscoveryWindowInterval, lhs.mDiscoveryWindowInterval);
}
@@ -153,6 +166,7 @@ public final class ConfigRequest implements Parcelable {
int result = 17;
result = 31 * result + (mSupport5gBand ? 1 : 0);
+ result = 31 * result + (mSupport6gBand ? 1 : 0);
result = 31 * result + mMasterPreference;
result = 31 * result + mClusterLow;
result = 31 * result + mClusterHigh;
@@ -190,9 +204,9 @@ public final class ConfigRequest implements Parcelable {
throw new IllegalArgumentException(
"Invalid argument combination - must have Cluster Low <= Cluster High");
}
- if (mDiscoveryWindowInterval.length != 2) {
+ if (mDiscoveryWindowInterval.length != 3) {
throw new IllegalArgumentException(
- "Invalid discovery window interval: must have 2 elements (2.4 & 5");
+ "Invalid discovery window interval: must have 3 elements (2.4 & 5 & 6");
}
if (mDiscoveryWindowInterval[NAN_BAND_24GHZ] != DW_INTERVAL_NOT_INIT &&
(mDiscoveryWindowInterval[NAN_BAND_24GHZ] < 1 // valid for 2.4GHz: [1-5]
@@ -206,7 +220,12 @@ public final class ConfigRequest implements Parcelable {
throw new IllegalArgumentException(
"Invalid discovery window interval for 5GHz: valid is UNSET or [0,5]");
}
-
+ if (mDiscoveryWindowInterval[NAN_BAND_6GHZ] != DW_INTERVAL_NOT_INIT
+ && (mDiscoveryWindowInterval[NAN_BAND_6GHZ] < 0 // valid for 6GHz: [0-5]
+ || mDiscoveryWindowInterval[NAN_BAND_6GHZ] > 5)) {
+ throw new IllegalArgumentException(
+ "Invalid discovery window interval for 6GHz: valid is UNSET or [0,5]");
+ }
}
/**
@@ -214,10 +233,12 @@ public final class ConfigRequest implements Parcelable {
*/
public static final class Builder {
private boolean mSupport5gBand = true;
+ private boolean mSupport6gBand = false;
private int mMasterPreference = 0;
private int mClusterLow = CLUSTER_ID_MIN;
private int mClusterHigh = CLUSTER_ID_MAX;
- private int mDiscoveryWindowInterval[] = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT};
+ private int[] mDiscoveryWindowInterval = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT,
+ DW_INTERVAL_NOT_INIT};
/**
* Specify whether 5G band support is required in this request. Disabled by default.
@@ -233,6 +254,19 @@ public final class ConfigRequest implements Parcelable {
}
/**
+ * Specify whether 6G band support is required in this request. Disabled by default.
+ *
+ * @param support6gBand Support for 6G band is required.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setSupport6gBand(boolean support6gBand) {
+ mSupport6gBand = support6gBand;
+ return this;
+ }
+
+ /**
* Specify the Master Preference requested. The permitted range is 0 (the default) to
* 255 with 1 and 255 excluded (reserved).
*
@@ -310,7 +344,8 @@ public final class ConfigRequest implements Parcelable {
* awake. The configuration enables trading off latency vs. power (higher interval means
* higher discovery latency but lower power).
*
- * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ}.
+ * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ} or
+ * {@link #NAN_BAND_6GHZ}.
* @param interval A value of 1, 2, 3, 4, or 5 indicating an interval of 2^(interval-1). For
* the 5GHz band a value of 0 indicates that the device will not be awake
* for any discovery windows.
@@ -319,13 +354,14 @@ public final class ConfigRequest implements Parcelable {
* {@code builder.setDiscoveryWindowInterval(...).setMasterPreference(...)}.
*/
public Builder setDiscoveryWindowInterval(int band, int interval) {
- if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ) {
+ if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ && band != NAN_BAND_6GHZ) {
throw new IllegalArgumentException("Invalid band value");
}
if ((band == NAN_BAND_24GHZ && (interval < 1 || interval > 5))
- || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))) {
+ || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))
+ || (band == NAN_BAND_6GHZ && (interval < 0 || interval > 5))) {
throw new IllegalArgumentException(
- "Invalid interval value: 2.4 GHz [1,5] or 5GHz [0,5]");
+ "Invalid interval value: 2.4 GHz [1,5] or 5GHz/6GHz [0,5]");
}
mDiscoveryWindowInterval[band] = interval;
@@ -342,8 +378,8 @@ public final class ConfigRequest implements Parcelable {
"Invalid argument combination - must have Cluster Low <= Cluster High");
}
- return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh,
- mDiscoveryWindowInterval);
+ return new ConfigRequest(mSupport5gBand, mSupport6gBand, mMasterPreference, mClusterLow,
+ mClusterHigh, mDiscoveryWindowInterval);
}
}
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 2c27037d29aa..524a53c03305 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -547,19 +547,6 @@ public class BaseWifiService extends IWifiManager.Stub {
throw new UnsupportedOperationException();
}
- /** @deprecated replaced by {@link #registerScanResultsCallback(IScanResultsCallback)} */
- @Deprecated
- public void registerScanResultsListener(
- IBinder binder, IScanResultsListener listener, int listenerIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated replaced by {@link #unregisterScanResultsCallback(IScanResultsCallback)} */
- @Deprecated
- public void unregisterScanResultsListener(int listenerIdentifier) {
- throw new UnsupportedOperationException();
- }
-
@Override
public void registerScanResultsCallback(IScanResultsCallback callback) {
throw new UnsupportedOperationException();
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 200c0e31cb22..65fbf5b099d4 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -578,12 +578,15 @@ public class WifiAwareManagerTest {
collector.checkThat("mMasterPreference", 0,
equalTo(configRequest.mMasterPreference));
collector.checkThat("mSupport5gBand", true, equalTo(configRequest.mSupport5gBand));
- collector.checkThat("mDiscoveryWindowInterval.length", 2,
+ collector.checkThat("mSupport6gBand", false, equalTo(configRequest.mSupport6gBand));
+ collector.checkThat("mDiscoveryWindowInterval.length", 3,
equalTo(configRequest.mDiscoveryWindowInterval.length));
collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]));
collector.checkThat("mDiscoveryWindowInterval[5Hz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]));
+ collector.checkThat("mDiscoveryWindowInterval[6Hz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
+ equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_6GHZ]));
}
@Test
@@ -592,12 +595,16 @@ public class WifiAwareManagerTest {
final int clusterLow = 5;
final int masterPreference = 55;
final boolean supportBand5g = true;
+ final boolean supportBand6g = true;
final int dwWindow5GHz = 3;
+ final int dwWindow6GHz = 4;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
.setClusterLow(clusterLow).setMasterPreference(masterPreference)
.setSupport5gBand(supportBand5g)
+ .setSupport6gBand(supportBand6g)
.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz)
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_6GHZ, dwWindow6GHz)
.build();
collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh));
@@ -605,12 +612,15 @@ public class WifiAwareManagerTest {
collector.checkThat("mMasterPreference", masterPreference,
equalTo(configRequest.mMasterPreference));
collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand));
- collector.checkThat("mDiscoveryWindowInterval.length", 2,
+ collector.checkThat("mSupport6gBand", supportBand6g, equalTo(configRequest.mSupport6gBand));
+ collector.checkThat("mDiscoveryWindowInterval.length", 3,
equalTo(configRequest.mDiscoveryWindowInterval.length));
collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]));
collector.checkThat("mDiscoveryWindowInterval[5GHz]", dwWindow5GHz,
equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]));
+ collector.checkThat("mDiscoveryWindowInterval[6GHz]", dwWindow6GHz,
+ equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_6GHZ]));
}
@Test(expected = IllegalArgumentException.class)
@@ -689,14 +699,18 @@ public class WifiAwareManagerTest {
final int clusterLow = 25;
final int masterPreference = 177;
final boolean supportBand5g = true;
+ final boolean supportBand6g = false;
final int dwWindow24GHz = 1;
final int dwWindow5GHz = 5;
+ final int dwWindow6GHz = 4;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
.setClusterLow(clusterLow).setMasterPreference(masterPreference)
.setSupport5gBand(supportBand5g)
+ .setSupport6gBand(supportBand6g)
.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwWindow24GHz)
.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz)
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_6GHZ, dwWindow6GHz)
.build();
Parcel parcelW = Parcel.obtain();